From 64d17728420bb6ca9bcdca4e29615fc19287d033 Mon Sep 17 00:00:00 2001 From: seb Date: Mon, 12 Oct 2026 18:36:57 +0200 Subject: [PATCH] u --- API.category.md | 6 ++-- API.company.md | 4 +-- API.md | 4 +-- src/providers/mssql/queries/category.js | 21 +++++++++++++- src/providers/mssql/queries/company.js | 2 ++ src/providers/sqlite3/queries/category.js | 21 +++++++++++++- src/store/category/getTreeForCompany.js | 34 ++++++++++++++--------- src/store/company/list.js | 4 +-- 8 files changed, 72 insertions(+), 24 deletions(-) diff --git a/API.category.md b/API.category.md index ddee9d8..e7a7bf2 100644 --- a/API.category.md +++ b/API.category.md @@ -171,7 +171,7 @@ const articles = await store.category.getCategoryArticles(42, 5); *Source: `src/store/category/getTreeForCompany.js`* -Returns the full category tree for the user's selected company together with its articles and the posterminal ids assigned to each category. +Returns the full category tree for the user's selected company together with the article count and the posterminal ids assigned to each category. **Parameters** @@ -182,13 +182,13 @@ Returns the full category tree for the user's selected company together with its **Returns** -`Promise<{languageId: number, tree: Array, articles: Array}>` — The category tree, articles and language. +`Promise<{languageId: number, tree: Array<{id: number, parentId: number|null, sort: number, name: string|null, slug: string|null, fullHash: string|null, thumbW: number|null, thumbH: number|null, thumbHash: string|null, articleCount: number, posterminalIds: number[]}>, articleCount: number}>` — The category tree and article count. **Example** ```js const tree = await store.category.getTreeForCompany(42); -// => { languageId, tree: [{ id, posterminalIds, ... }], articles: [...] } +// => { languageId, tree: [{ id, articleCount, posterminalIds, ... }], articleCount: 25 } ``` ## `category.move` diff --git a/API.company.md b/API.company.md index 24dc7e4..26619c0 100644 --- a/API.company.md +++ b/API.company.md @@ -186,13 +186,13 @@ Lists the companies accessible to the given Google auth user. **Returns** -`Promise>` — The accessible company rows. +`Promise>` — The accessible company rows. **Example** ```js const companies = await store.company.list(42); -// => [{ id, name, ... }] +// => [{ id, name, rootCategoryId, languageId, iso639, taxrate, reducedtaxrate, taxfreerate }] ``` ## `company.update` diff --git a/API.md b/API.md index c4e7c61..3f9edd1 100644 --- a/API.md +++ b/API.md @@ -72,7 +72,7 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met - `getById(id: number, tx?: object): Promise` - `getByName(name: string, tx?: object): Promise` - `getHistory(googleAuthId: number, opts?: {limit?: number}, tx?: object): Promise>` - - `list(googleAuthId: number, tx?: object): Promise>` + - `list(googleAuthId: number, tx?: object): Promise>` - `update(id: number, input: {iso639: string, name: string, taxrate: number, reducedtaxrate: number}, tx?: object): Promise<{id: number, rootCategoryId: number, languageId: number, taxrateId: number, reducedtaxrateId: number, taxfreerateId: number}>` - [category](API.category.md) - `addImage(googleAuthId: number, categoryId: number, image: Buffer, tx?: object): Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string, smallThumbW: number, smallThumbH: number}>` @@ -81,7 +81,7 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met - `deleteImage(googleAuthId: number, categoryId: number, tx?: object): Promise<{id: number, thumbW: null, thumbH: null, thumbHash: null}>` - `ensureByPath(input: {googleAuthId: number, parentId: number, path: string}, tx?: object): Promise` - `getCategoryArticles(googleAuthId: number, categoryId: number, tx?: object): Promise>` - - `getTreeForCompany(googleAuthId: number, tx?: object): Promise<{languageId: number, tree: Array, articles: Array}>` + - `getTreeForCompany(googleAuthId: number, tx?: object): Promise<{languageId: number, tree: Array<{id: number, parentId: number|null, sort: number, name: string|null, slug: string|null, fullHash: string|null, thumbW: number|null, thumbH: number|null, thumbHash: string|null, articleCount: number, posterminalIds: number[]}>, articleCount: number}>` - `move(input: {googleAuthId: number, id: number, parentId: number}, tx?: object): Promise<{id: number, parentId: number, sort: number}>` - `order(input: {googleAuthId: number, parentId: number, categoryIds: Array}, tx?: object): Promise<{categoryIds: Array}>` - `rename(input: {googleAuthId: number, id: number, name: string}, tx?: object): Promise<{id: number, name: string, slug: string}>` diff --git a/src/providers/mssql/queries/category.js b/src/providers/mssql/queries/category.js index 01a0881..d300337 100644 --- a/src/providers/mssql/queries/category.js +++ b/src/providers/mssql/queries/category.js @@ -19,7 +19,13 @@ SELECT p.hash AS fullHash, p.thumbw AS thumbW, p.thumbh AS thumbH, - p.thumbhash AS thumbHash + p.thumbhash AS thumbHash, + ( + SELECT COUNT(*) + FROM categoryarticle ca + INNER JOIN article a ON a.id = ca.article_id + WHERE ca.category_id = t.id AND a.parentarticle_id IS NULL + ) AS articleCount FROM tree t LEFT JOIN categorytext ct ON ct.category_id = t.id AND ct.language_id = @languageId @@ -33,6 +39,19 @@ LEFT JOIN pic p ON p.id = first_pic.pic_id ORDER BY t.sort, t.id `; +export const COUNT_ARTICLES_IN_TREE = ` +WITH tree AS ( + SELECT id FROM category WHERE id = @rootId + UNION ALL + SELECT c.id FROM category c INNER JOIN tree t ON c.parent_id = t.id +) +SELECT COUNT(DISTINCT ca.article_id) AS total +FROM categoryarticle ca +INNER JOIN article a ON a.id = ca.article_id +WHERE ca.category_id IN (SELECT id FROM tree) + AND a.parentarticle_id IS NULL +`; + import { ARTICLE_LIST_COLUMNS, ARTICLE_LIST_JOINS, diff --git a/src/providers/mssql/queries/company.js b/src/providers/mssql/queries/company.js index 87f925f..73d0d1b 100644 --- a/src/providers/mssql/queries/company.js +++ b/src/providers/mssql/queries/company.js @@ -52,11 +52,13 @@ SELECT c.name, c.root_category_id AS rootCategoryId, c.default_language_id AS languageId, + l.iso639, tr.rate AS taxrate, rtr.rate AS reducedtaxrate, tfr.rate AS taxfreerate FROM company c INNER JOIN companyuser cu ON cu.company_id = c.id +LEFT JOIN language l ON l.id = c.default_language_id LEFT JOIN tax tr ON tr.id = c.taxrate LEFT JOIN tax rtr ON rtr.id = c.reducedtaxrate LEFT JOIN tax tfr ON tfr.id = c.taxfreerate diff --git a/src/providers/sqlite3/queries/category.js b/src/providers/sqlite3/queries/category.js index ce09f2c..8200dc4 100644 --- a/src/providers/sqlite3/queries/category.js +++ b/src/providers/sqlite3/queries/category.js @@ -79,7 +79,13 @@ WITH RECURSIVE tree AS ( SELECT t.id, t.parent_id AS parentId, t.sort, ct.name, ct.slug, p.hash AS fullHash, p.thumbw AS thumbW, p.thumbh AS thumbH, - p.thumbhash AS thumbHash + p.thumbhash AS thumbHash, + ( + SELECT COUNT(*) + FROM categoryarticle ca + INNER JOIN article a ON a.id = ca.article_id + WHERE ca.category_id = t.id AND a.parentarticle_id IS NULL + ) AS articleCount FROM tree t LEFT JOIN categorytext ct ON ct.category_id = t.id AND ct.language_id = @languageId @@ -89,4 +95,17 @@ LEFT JOIN pic p ON p.id = ( ORDER BY cp.sort, cp.id LIMIT 1 ) ORDER BY t.sort, t.id +`; + +export const COUNT_ARTICLES_IN_TREE = ` +WITH RECURSIVE tree AS ( + SELECT id FROM category WHERE id = @rootId + UNION ALL + SELECT c.id FROM category c INNER JOIN tree t ON c.parent_id = t.id +) +SELECT COUNT(DISTINCT ca.article_id) AS total +FROM categoryarticle ca +INNER JOIN article a ON a.id = ca.article_id +WHERE ca.category_id IN (SELECT id FROM tree) + AND a.parentarticle_id IS NULL `; \ No newline at end of file diff --git a/src/store/category/getTreeForCompany.js b/src/store/category/getTreeForCompany.js index 26c5abc..9885b38 100644 --- a/src/store/category/getTreeForCompany.js +++ b/src/store/category/getTreeForCompany.js @@ -1,52 +1,60 @@ -import { GET_TREE_FOR_COMPANY as MSSQL_GET_TREE } from '../../providers/mssql/queries/category.js'; -import { GET_TREE_FOR_COMPANY as SQLITE_GET_TREE } from '../../providers/sqlite3/queries/category.js'; +import { + GET_TREE_FOR_COMPANY as MSSQL_GET_TREE, + COUNT_ARTICLES_IN_TREE as MSSQL_COUNT_ARTICLES, +} from '../../providers/mssql/queries/category.js'; +import { + GET_TREE_FOR_COMPANY as SQLITE_GET_TREE, + COUNT_ARTICLES_IN_TREE as SQLITE_COUNT_ARTICLES, +} from '../../providers/sqlite3/queries/category.js'; import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js'; -import queryCategoryArticles from './queryCategoryArticles.js'; import getCategoryPosterminalIds from '../posterminal/getCategoryPosterminalIds.js'; import makeRunner from '../runner.js'; export default function getTreeForCompany(provider) { const access = resolveSelectedCompanyAccess(provider); - const articles = queryCategoryArticles(provider); const posterminalIds = getCategoryPosterminalIds(provider); - const sql = - provider?.dialect === 'sqlite3' ? SQLITE_GET_TREE : MSSQL_GET_TREE; + const isSqlite = provider?.dialect === 'sqlite3'; + const sql = isSqlite ? SQLITE_GET_TREE : MSSQL_GET_TREE; + const countSql = isSqlite ? SQLITE_COUNT_ARTICLES : MSSQL_COUNT_ARTICLES; /** * Returns the full category tree for the user's selected company together - * with its articles and the posterminal ids assigned to each category. + * with the article count and the posterminal ids assigned to each category. * * @param {number} googleAuthId - The id of the Google auth user. * @param {object} [tx] - Optional transaction to run within. - * @returns {Promise<{languageId: number, tree: Array, articles: Array}>} The category tree, articles and language. + * @returns {Promise<{languageId: number, tree: Array<{id: number, parentId: number|null, sort: number, name: string|null, slug: string|null, fullHash: string|null, thumbW: number|null, thumbH: number|null, thumbHash: string|null, articleCount: number, posterminalIds: number[]}>, articleCount: number}>} The category tree and article count. * * @example * const tree = await store.category.getTreeForCompany(42); - * // => { languageId, tree: [{ id, posterminalIds, ... }], articles: [...] } + * // => { languageId, tree: [{ id, articleCount, posterminalIds, ... }], articleCount: 25 } */ return async (googleAuthId, tx) => { - const { companyId, rootCategoryId, languageId, posCustomerGroupId } = + const { companyId, rootCategoryId, languageId } = await access(googleAuthId, tx); const runner = makeRunner(provider, tx); - const [treeResult, articleRows, ptMap] = await Promise.all([ + const [treeResult, countResult, ptMap] = await Promise.all([ runner.query(sql, { rootId: rootCategoryId, languageId, }), - articles({ categoryId: rootCategoryId, languageId, posCustomerGroupId }, tx), + runner.query(countSql, { + rootId: rootCategoryId, + }), posterminalIds(companyId, tx), ]); const tree = treeResult.rows.map((row) => ({ ...row, + articleCount: Number(row.articleCount ?? 0), posterminalIds: ptMap[row.id] ?? [], })); return { languageId, tree, - articles: articleRows, + articleCount: Number(countResult.rows[0]?.total ?? 0), }; }; } diff --git a/src/store/company/list.js b/src/store/company/list.js index 71eb9b7..67e40ee 100644 --- a/src/store/company/list.js +++ b/src/store/company/list.js @@ -10,11 +10,11 @@ export default function list(provider) { * * @param {number} googleAuthId - The id of the Google auth user. * @param {object} [tx] - Optional transaction to run within. - * @returns {Promise>} The accessible company rows. + * @returns {Promise>} The accessible company rows. * * @example * const companies = await store.company.list(42); - * // => [{ id, name, ... }] + * // => [{ id, name, rootCategoryId, languageId, iso639, taxrate, reducedtaxrate, taxfreerate }] */ return async (googleAuthId, tx) => { const result = await makeRunner(provider, tx).query(sql, { googleAuthId });