This commit is contained in:
seb
2026-10-12 18:36:57 +02:00
parent 10b1d53f9d
commit 64d1772842
8 changed files with 72 additions and 24 deletions

View File

@@ -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<object>, articles: Array<object>}>` — 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`

View File

@@ -186,13 +186,13 @@ Lists the companies accessible to the given Google auth user.
**Returns**
`Promise<Array<object>>` — The accessible company rows.
`Promise<Array<{id: number, name: string, rootCategoryId: number, languageId: number, iso639: string|null, taxrate: number, reducedtaxrate: number, taxfreerate: number}>>` — 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`

4
API.md
View File

@@ -72,7 +72,7 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
- `getById(id: number, tx?: object): Promise<object|undefined>`
- `getByName(name: string, tx?: object): Promise<object|undefined>`
- `getHistory(googleAuthId: number, opts?: {limit?: number}, tx?: object): Promise<Array<{id: number, companyuserId: number|null, date: string, text: string}>>`
- `list(googleAuthId: number, tx?: object): Promise<Array<object>>`
- `list(googleAuthId: number, tx?: object): Promise<Array<{id: number, name: string, rootCategoryId: number, languageId: number, iso639: string|null, taxrate: number, reducedtaxrate: number, taxfreerate: number}>>`
- `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<number>`
- `getCategoryArticles(googleAuthId: number, categoryId: number, tx?: object): Promise<Array<{id: number, sku: string, name: string, slug: string, net: number|null, taxRate: number|null, optionsCount: number, articleListCount: number, childCount: number, parentArticleId: number|null, parentSku: string|null, fullHash: string|null, thumbW: number|null, thumbH: number|null, thumbHash: string|null, brand: {id: number|null, name: string, fullHash: string|null, thumbW: number|null, thumbH: number|null, thumbHash: string|null}|null}>>`
- `getTreeForCompany(googleAuthId: number, tx?: object): Promise<{languageId: number, tree: Array<object>, articles: Array<object>}>`
- `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<number>}, tx?: object): Promise<{categoryIds: Array<number>}>`
- `rename(input: {googleAuthId: number, id: number, name: string}, tx?: object): Promise<{id: number, name: string, slug: string}>`

View File

@@ -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,

View File

@@ -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

View File

@@ -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
`;

View File

@@ -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<object>, articles: Array<object>}>} 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),
};
};
}

View File

@@ -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<Array<object>>} The accessible company rows.
* @returns {Promise<Array<{id: number, name: string, rootCategoryId: number, languageId: number, iso639: string|null, taxrate: number, reducedtaxrate: number, taxfreerate: number}>>} 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 });