Compare commits

..

2 Commits

Author SHA1 Message Date
seb
e3761bc48b u 2026-10-20 23:19:43 +02:00
seb
813e7c89b8 u 2026-10-20 23:16:47 +02:00
7 changed files with 29 additions and 4 deletions

View File

@@ -158,7 +158,7 @@ Returns the articles belonging to a single category.
**Returns** **Returns**
`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, articlewarehousesummary: Array<{id: number, articleId: number, warehouseId: number, warehouseName: string, stock: number}>, warehouses: Array<{id: number, name: string, stock: number}>}>>` — The articles in the category. `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, smallThumbW: number|null, smallThumbH: number|null, smallThumbBinary: string|null, brand: {id: number|null, name: string, fullHash: string|null, thumbW: number|null, thumbH: number|null, thumbHash: string|null}|null, articlewarehousesummary: Array<{id: number, articleId: number, warehouseId: number, warehouseName: string, stock: number}>, warehouses: Array<{id: number, name: string, stock: number}>}>>` — The articles in the category.
**Example** **Example**

2
API.md
View File

@@ -99,7 +99,7 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
- `delete(input: {googleAuthId: number, id: number, recursive?: boolean}, tx?: object): Promise<void>` - `delete(input: {googleAuthId: number, id: number, recursive?: boolean}, tx?: object): Promise<void>`
- `deleteImage(googleAuthId: number, categoryId: number, tx?: object): Promise<{id: number, thumbW: null, thumbH: null, thumbHash: null}>` - `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>` - `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, articlewarehousesummary: Array<{id: number, articleId: number, warehouseId: number, warehouseName: string, stock: number}>, warehouses: Array<{id: number, name: string, stock: 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, smallThumbW: number|null, smallThumbH: number|null, smallThumbBinary: string|null, brand: {id: number|null, name: string, fullHash: string|null, thumbW: number|null, thumbH: number|null, thumbHash: string|null}|null, articlewarehousesummary: Array<{id: number, articleId: number, warehouseId: number, warehouseName: string, stock: number}>, warehouses: Array<{id: number, name: string, stock: number}>}>>`
- `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}>` - `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}>` - `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>}>` - `order(input: {googleAuthId: number, parentId: number, categoryIds: Array<number>}, tx?: object): Promise<{categoryIds: Array<number>}>`

View File

@@ -60,6 +60,9 @@ export const ARTICLE_LIST_COLUMNS = `
pic.thumbw AS thumbW, pic.thumbw AS thumbW,
pic.thumbh AS thumbH, pic.thumbh AS thumbH,
pic.thumbhash AS thumbHash, pic.thumbhash AS thumbHash,
pic.smallthumbw AS smallThumbW,
pic.smallthumbh AS smallThumbH,
pic.smallthumbbinary AS smallThumbBinary,
b.id AS brandId, b.id AS brandId,
b.name AS brandName, b.name AS brandName,
brand_pic.hash AS brandFullHash, brand_pic.hash AS brandFullHash,

View File

@@ -249,7 +249,11 @@ export async function recordStockAdjustmentHistory(runner, { articleId, companyu
/** Records price changes in article history. */ /** Records price changes in article history. */
export async function recordPriceChangeHistory(runner, { articleId, companyuserId, date, oldPrices, newPrices }) { export async function recordPriceChangeHistory(runner, { articleId, companyuserId, date, oldPrices, newPrices }) {
const fmtPrice = (p) => `${p.net ?? p.price}@${p.taxRate ?? p.tax ?? 0}%`; const fmtPrice = (p) => {
const net = p.net ?? p.price;
const tax = p.taxRate ?? p.tax ?? 0;
return `${formatCurrencyDe(calculateGross(net, 0, tax))}@${tax}%`;
};
const oldStr = (oldPrices || []).map(fmtPrice).join(', '); const oldStr = (oldPrices || []).map(fmtPrice).join(', ');
const newStr = (newPrices || []).map(fmtPrice).join(', '); const newStr = (newPrices || []).map(fmtPrice).join(', ');
if (oldStr === newStr) return; if (oldStr === newStr) return;

View File

@@ -11,7 +11,7 @@ export default function getCategoryArticles(provider) {
* @param {number} googleAuthId - The id of the Google auth user. * @param {number} googleAuthId - The id of the Google auth user.
* @param {number} categoryId - The id of the category. * @param {number} categoryId - The id of the category.
* @param {object} [tx] - Optional transaction to run within. * @param {object} [tx] - Optional transaction to run within.
* @returns {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, articlewarehousesummary: Array<{id: number, articleId: number, warehouseId: number, warehouseName: string, stock: number}>, warehouses: Array<{id: number, name: string, stock: number}>}>>} The articles in the category. * @returns {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, smallThumbW: number|null, smallThumbH: number|null, smallThumbBinary: string|null, brand: {id: number|null, name: string, fullHash: string|null, thumbW: number|null, thumbH: number|null, thumbHash: string|null}|null, articlewarehousesummary: Array<{id: number, articleId: number, warehouseId: number, warehouseName: string, stock: number}>, warehouses: Array<{id: number, name: string, stock: number}>}>>} The articles in the category.
* *
* @example * @example
* const articles = await store.category.getCategoryArticles(42, 5); * const articles = await store.category.getCategoryArticles(42, 5);

View File

@@ -56,6 +56,13 @@ export default function queryCategoryArticles(provider) {
const summaries = summaryMap.get(row.id) ?? []; const summaries = summaryMap.get(row.id) ?? [];
return { return {
...rest, ...rest,
smallThumbW: rest.smallThumbW != null ? Number(rest.smallThumbW) : null,
smallThumbH: rest.smallThumbH != null ? Number(rest.smallThumbH) : null,
smallThumbBinary: rest.smallThumbBinary
? (Buffer.isBuffer(rest.smallThumbBinary)
? rest.smallThumbBinary.toString('base64')
: String(rest.smallThumbBinary))
: null,
track_stock: Boolean(rest.track_stock ?? 0), track_stock: Boolean(rest.track_stock ?? 0),
// MSSQL returns BIT as boolean, SQLite as 0/1 integer. Normalize so both // MSSQL returns BIT as boolean, SQLite as 0/1 integer. Normalize so both
// dialects expose isParent as a real boolean, matching article/getBySku. // dialects expose isParent as a real boolean, matching article/getBySku.

View File

@@ -90,8 +90,19 @@ describe('store.category', () => {
assert.equal(list[0].sku, art.sku); assert.equal(list[0].sku, art.sku);
assert.equal(list[0].name, 'Listed'); assert.equal(list[0].name, 'Listed');
assert.equal(list[0].parentArticleId, null); assert.equal(list[0].parentArticleId, null);
assert.equal(list[0].smallThumbW, null);
assert.equal(list[0].smallThumbH, null);
assert.equal(list[0].smallThumbBinary, null);
assert(Array.isArray(list[0].articlewarehousesummary)); assert(Array.isArray(list[0].articlewarehousesummary));
assert(Array.isArray(list[0].warehouses)); assert(Array.isArray(list[0].warehouses));
await store.article.addImage(tenant.googleAuthId, art.sku, await makePng());
const listWithImg = await store.category.getCategoryArticles(tenant.googleAuthId, cat.id);
assert.equal(listWithImg.length, 1);
assert.ok(listWithImg[0].smallThumbW > 0);
assert.ok(listWithImg[0].smallThumbH > 0);
assert.equal(typeof listWithImg[0].smallThumbBinary, 'string');
assert.ok(listWithImg[0].smallThumbBinary.length > 0);
}); });
it('rejects a category of another company', async () => { it('rejects a category of another company', async () => {