This commit is contained in:
seb
2026-08-04 01:24:27 +02:00
parent d313fc7a85
commit 82c61e599d
4 changed files with 214 additions and 1 deletions

View File

@@ -0,0 +1,93 @@
import { GET_ARTICLE_EXPORT, GET_CATEGORY_TREE_FLAT } from '../../sql/article.js';
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
import resolveCategoryAccess from '../category/resolveCategoryAccess.js';
const ROOT_CATEGORY_NAME = 'Home';
function parseJsonArray(raw) {
if (!raw) return [];
const arr = typeof raw === 'string' ? JSON.parse(raw) : raw;
return Array.isArray(arr) ? arr : [];
}
export default function listExport(pool) {
const companyAccess = resolveCompanyAccess(pool);
const categoryAccess = resolveCategoryAccess(pool);
return async (googleAuthId, companyId, categoryId) => {
const { languageId, posCustomerGroupId, rootCategoryId } = await companyAccess(
googleAuthId,
companyId,
);
// Default to the company's root category when no specific category is given.
let exportCategoryId = rootCategoryId;
if (categoryId != null) {
// The requested category must be reachable via the selected company's root.
const access = await categoryAccess(googleAuthId, categoryId);
if (access.rootCategoryId !== rootCategoryId) {
throw new Error('Not authorized');
}
exportCategoryId = categoryId;
}
const [result, treeResult] = await Promise.all([
pool
.request()
.input('categoryId', exportCategoryId)
.input('languageId', languageId)
.input('posCustomerGroupId', posCustomerGroupId)
.query(GET_ARTICLE_EXPORT),
pool
.request()
.input('rootId', rootCategoryId)
.input('languageId', languageId)
.query(GET_CATEGORY_TREE_FLAT),
]);
// id -> { id, parentId, name } for breadcrumb resolution.
const catById = new Map();
for (const node of treeResult.recordset) {
catById.set(String(node.id), node);
}
// Breadcrumb label for one category id: "Home" for root, else trimmed name.
const displayName = (node) =>
String(node.id) === String(rootCategoryId) ? ROOT_CATEGORY_NAME : (node.name ?? '').trim();
const breadcrumb = (categoryId) => {
const crumbs = [];
const seen = new Set();
let id = categoryId;
while (id != null && !seen.has(String(id))) {
seen.add(String(id));
const node = catById.get(String(id));
if (!node) break;
crumbs.unshift(displayName(node));
id = node.parentId;
}
// A category outside the tree still yields its own label.
return crumbs.join('·');
};
return result.recordset.map((row) => ({
id: row.id,
sku: row.sku,
name: row.name,
slug: row.slug,
unitSi: row.unitSi,
unitSymbol: row.unitSymbol,
unitamount: row.unitamount,
brandName: row.brandName,
net: row.net,
taxRate: row.taxRate,
eans: parseJsonArray(row.eansJson).map((r) => r.ean),
categoryIds: parseJsonArray(row.categoryIdsJson).map((r) => r.category_id),
categoryBreadcrumbs: parseJsonArray(row.categoryIdsJson)
.map((r) => breadcrumb(r.category_id))
.filter(Boolean),
fullHashes: parseJsonArray(row.fullHashesJson).map((r) => r.hash),
}));
};
}