i
This commit is contained in:
@@ -82,11 +82,19 @@ FROM article
|
|||||||
WHERE sku = @sku AND (@companyId IS NULL OR company_id = @companyId)
|
WHERE sku = @sku AND (@companyId IS NULL OR company_id = @companyId)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
/** True if an EAN is already assigned to any article (duplicate guard). */
|
/** True if an EAN is already assigned to any article in the company (duplicate guard). */
|
||||||
export const EAN_EXISTS = `
|
export const EAN_EXISTS = `
|
||||||
SELECT TOP 1 1 AS used
|
SELECT TOP 1 1 AS used
|
||||||
FROM articleean
|
FROM articleean ae
|
||||||
WHERE ean = @ean
|
INNER JOIN article a ON a.id = ae.article_id
|
||||||
|
WHERE ae.ean = @ean AND (@companyId IS NULL OR a.company_id = @companyId)
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const EAN_EXISTS_FOR_OTHER_ARTICLE = `
|
||||||
|
SELECT TOP 1 1 AS used
|
||||||
|
FROM articleean ae
|
||||||
|
INNER JOIN article a ON a.id = ae.article_id
|
||||||
|
WHERE ae.ean = @ean AND a.company_id = @companyId AND a.id <> @articleId
|
||||||
`;
|
`;
|
||||||
|
|
||||||
/** Link an article to its parent article and mark the parent. */
|
/** Link an article to its parent article and mark the parent. */
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ VALUES (
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const GET_CATEGORY_ID_BY_HASH = `
|
export const GET_CATEGORY_ID_BY_HASH = `
|
||||||
SELECT TOP 1 categoryId
|
SELECT categoryId
|
||||||
FROM (
|
FROM (
|
||||||
SELECT ca.category_id AS categoryId
|
SELECT ca.category_id AS categoryId
|
||||||
FROM pic p
|
FROM pic p
|
||||||
@@ -19,6 +19,15 @@ FROM (
|
|||||||
|
|
||||||
UNION
|
UNION
|
||||||
|
|
||||||
|
SELECT c.root_category_id AS categoryId
|
||||||
|
FROM pic p
|
||||||
|
INNER JOIN articlepic ap ON ap.pic_id = p.id
|
||||||
|
INNER JOIN article a ON a.id = ap.article_id
|
||||||
|
INNER JOIN company c ON c.id = a.company_id
|
||||||
|
WHERE p.hash = @hash
|
||||||
|
|
||||||
|
UNION
|
||||||
|
|
||||||
SELECT cp.category_id AS categoryId
|
SELECT cp.category_id AS categoryId
|
||||||
FROM pic p
|
FROM pic p
|
||||||
INNER JOIN categorypic cp ON cp.pic_id = p.id
|
INNER JOIN categorypic cp ON cp.pic_id = p.id
|
||||||
@@ -33,6 +42,7 @@ FROM (
|
|||||||
INNER JOIN company c ON c.id = b.company_id
|
INNER JOIN company c ON c.id = b.company_id
|
||||||
WHERE p.hash = @hash
|
WHERE p.hash = @hash
|
||||||
) AS combined
|
) AS combined
|
||||||
|
WHERE categoryId IS NOT NULL
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const GET_FULL_BY_HASH = `
|
export const GET_FULL_BY_HASH = `
|
||||||
|
|||||||
@@ -418,11 +418,13 @@ BEGIN
|
|||||||
article_id INT NOT NULL,
|
article_id INT NOT NULL,
|
||||||
ean NVARCHAR(50) NOT NULL,
|
ean NVARCHAR(50) NOT NULL,
|
||||||
CONSTRAINT FK_articleean_article FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE,
|
CONSTRAINT FK_articleean_article FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE,
|
||||||
CONSTRAINT UQ_articleean_ean UNIQUE (ean)
|
CONSTRAINT UQ_articleean_article_ean UNIQUE (article_id, ean)
|
||||||
);
|
);
|
||||||
-- Hot path: getBySku EANs by article (UQ is ean-leading only)
|
-- Hot path: getBySku EANs by article
|
||||||
CREATE INDEX IX_articleean_article_ean
|
CREATE INDEX IX_articleean_article_ean
|
||||||
ON articleean (article_id, ean);
|
ON articleean (article_id, ean);
|
||||||
|
CREATE INDEX IX_articleean_ean
|
||||||
|
ON articleean (ean);
|
||||||
END
|
END
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { translateQueries } from './translate.js';
|
|||||||
const queries = translateQueries(source, 'article');
|
const queries = translateQueries(source, 'article');
|
||||||
export const {
|
export const {
|
||||||
ADD_CATEGORY, ADD_EAN, ARTICLE_LIST_COLUMNS, CREATE, CREATE_CHILD,
|
ADD_CATEGORY, ADD_EAN, ARTICLE_LIST_COLUMNS, CREATE, CREATE_CHILD,
|
||||||
DELETE_CATEGORIES_BY_ARTICLE, DELETE_EANS_BY_ARTICLE, EAN_EXISTS,
|
DELETE_CATEGORIES_BY_ARTICLE, DELETE_EANS_BY_ARTICLE, EAN_EXISTS, EAN_EXISTS_FOR_OTHER_ARTICLE,
|
||||||
GET_BRAND_BY_COMPANY_AND_ID,
|
GET_BRAND_BY_COMPANY_AND_ID,
|
||||||
GET_CATEGORY_ID_BY_SKU, GET_CATEGORY_TREE_FLAT, GET_ID_BY_SKU,
|
GET_CATEGORY_ID_BY_SKU, GET_CATEGORY_TREE_FLAT, GET_ID_BY_SKU,
|
||||||
LIST_CHILDREN_BY_PARENT_ID,
|
LIST_CHILDREN_BY_PARENT_ID,
|
||||||
|
|||||||
@@ -246,11 +246,13 @@ export const INIT_ARTICLEEAN = `
|
|||||||
CREATE TABLE IF NOT EXISTS articleean (
|
CREATE TABLE IF NOT EXISTS articleean (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
article_id INTEGER NOT NULL,
|
article_id INTEGER NOT NULL,
|
||||||
ean TEXT NOT NULL UNIQUE,
|
ean TEXT NOT NULL,
|
||||||
FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE
|
FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
CREATE INDEX IF NOT EXISTS IX_articleean_article_ean
|
CREATE UNIQUE INDEX IF NOT EXISTS UQ_articleean_article_ean
|
||||||
ON articleean(article_id, ean);
|
ON articleean(article_id, ean);
|
||||||
|
CREATE INDEX IF NOT EXISTS IX_articleean_ean
|
||||||
|
ON articleean(ean);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const INIT_ARTICLEPIC = `
|
export const INIT_ARTICLEPIC = `
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
ADD_EAN as MSSQL_ADD_EAN,
|
ADD_EAN as MSSQL_ADD_EAN,
|
||||||
DELETE_EANS_BY_ARTICLE as MSSQL_DELETE_EANS,
|
DELETE_EANS_BY_ARTICLE as MSSQL_DELETE_EANS,
|
||||||
|
EAN_EXISTS_FOR_OTHER_ARTICLE as MSSQL_CHECK_CONFLICT,
|
||||||
} from '../../providers/mssql/queries/article.js';
|
} from '../../providers/mssql/queries/article.js';
|
||||||
import {
|
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,
|
||||||
|
EAN_EXISTS_FOR_OTHER_ARTICLE as SQLITE_CHECK_CONFLICT,
|
||||||
} from '../../providers/sqlite3/queries/article.js';
|
} from '../../providers/sqlite3/queries/article.js';
|
||||||
import { INSERT as MSSQL_INSERT_HISTORY } from '../../providers/mssql/queries/articlehistory.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 { INSERT as SQLITE_INSERT_HISTORY } from '../../providers/sqlite3/queries/articlehistory.js';
|
||||||
@@ -21,6 +23,7 @@ export default function setEansBySku(provider) {
|
|||||||
const addEanSql = isSqlite ? SQLITE_ADD_EAN : MSSQL_ADD_EAN;
|
const addEanSql = isSqlite ? SQLITE_ADD_EAN : MSSQL_ADD_EAN;
|
||||||
const deleteEansSql = isSqlite ? SQLITE_DELETE_EANS : MSSQL_DELETE_EANS;
|
const deleteEansSql = isSqlite ? SQLITE_DELETE_EANS : MSSQL_DELETE_EANS;
|
||||||
const insertHistorySql = isSqlite ? SQLITE_INSERT_HISTORY : MSSQL_INSERT_HISTORY;
|
const insertHistorySql = isSqlite ? SQLITE_INSERT_HISTORY : MSSQL_INSERT_HISTORY;
|
||||||
|
const checkConflictSql = isSqlite ? SQLITE_CHECK_CONFLICT : MSSQL_CHECK_CONFLICT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces the EAN list of an article by its SKU.
|
* Replaces the EAN list of an article by its SKU.
|
||||||
@@ -55,7 +58,19 @@ export default function setEansBySku(provider) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const runSet = async (runner) => {
|
const runSet = async (runner) => {
|
||||||
const { articleId, languageId, companyuserId } = await resolve(googleAuthId, sku, runner);
|
const { articleId, languageId, companyuserId, companyId } = await resolve(googleAuthId, sku, runner);
|
||||||
|
|
||||||
|
// Guard: EAN cannot be used by another article in the same company
|
||||||
|
for (const ean of normalized) {
|
||||||
|
const conflict = await runner.query(checkConflictSql, {
|
||||||
|
ean,
|
||||||
|
companyId,
|
||||||
|
articleId,
|
||||||
|
});
|
||||||
|
if (conflict.rows[0]) {
|
||||||
|
throw new Error('EAN already in use');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Read old EANs for history
|
// Read old EANs for history
|
||||||
const oldEanRes = await runner.query(
|
const oldEanRes = await runner.query(
|
||||||
|
|||||||
@@ -10,11 +10,26 @@ export default function resolvePicAccess(provider) {
|
|||||||
return async (googleAuthId, fullHash, tx) => {
|
return async (googleAuthId, fullHash, tx) => {
|
||||||
const result = await makeRunner(provider, tx).query(sql, { hash: fullHash });
|
const result = await makeRunner(provider, tx).query(sql, { hash: fullHash });
|
||||||
|
|
||||||
const categoryId = result.rows[0]?.categoryId;
|
if (!result.rows || result.rows.length === 0) {
|
||||||
if (!categoryId) {
|
|
||||||
throw new Error('Image not found');
|
throw new Error('Image not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
await access(googleAuthId, categoryId, tx);
|
let authorized = false;
|
||||||
|
let lastError = null;
|
||||||
|
for (const row of result.rows) {
|
||||||
|
const categoryId = row.categoryId;
|
||||||
|
if (!categoryId) continue;
|
||||||
|
try {
|
||||||
|
await access(googleAuthId, categoryId, tx);
|
||||||
|
authorized = true;
|
||||||
|
break;
|
||||||
|
} catch (err) {
|
||||||
|
lastError = err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!authorized) {
|
||||||
|
throw lastError || new Error('Not authorized');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user