From d313fc7a858ccedc05b80ffea6b69effae483273 Mon Sep 17 00:00:00 2001 From: seb Date: Mon, 3 Aug 2026 22:38:28 +0200 Subject: [PATCH] Rewrite article API docs with explicit TypeScript-style types Every method now shows typed signatures and return types. New SearchIndexEntry and ArticleDetail types replace the old vague shorthand. All prose paragraphs reference ArticleDetail instead of "same shape as getBySku". --- README.md | 121 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 81 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 5335c3c..b089aa6 100644 --- a/README.md +++ b/README.md @@ -263,71 +263,112 @@ Company-scoped POS terminals. Auth via `resolveCompanyAccess`. | Method | Signature | Returns | | --- | --- | --- | -| `listSearchIndex` | `(googleAuthId, companyId)` | `[{ id, sku, name, eans, categoryIds }]` | -| `createUnderParent` | `({ googleAuthId, parentId, name })` | `{ id, parentId, name, sku }` | -| `getBySku` | `(googleAuthId, sku)` | detail object (see below) | -| `updateBySku` | `(googleAuthId, sku, { name?, unitamount?, unitSi? })` | same shape as `getBySku` | -| `setPricesBySku` | `(googleAuthId, sku, [{ price, tax, minmultiplier? }])` | same shape as `getBySku` | -| `setEansBySku` | `(googleAuthId, sku, string[])` | same shape as `getBySku` | -| `setBrandIdBySku` | `(googleAuthId, sku, brandId \| null)` | same shape as `getBySku` | -| `setWeightBySku` | `(googleAuthId, sku, weight)` | same shape as `getBySku` | -| `setCategoryIdsBySku` | `(googleAuthId, sku, number[])` | same shape as `getBySku` | -| `addImage` | `(googleAuthId, sku, imageBuffer)` | image result (see below) | -| `deleteImage` | `(googleAuthId, sku, fullHash)` | `void` | -| `orderImages` | `(googleAuthId, sku, { fullHashes })` | `{ fullHashes }` | -| `delete` | `(googleAuthId, sku)` | `void` | +| `listSearchIndex` | `(googleAuthId: string, companyId: number)` | `SearchIndexEntry[]` (see below) | +| `createUnderParent` | `({ googleAuthId: string, parentId: number, name: string })` | `{ id: number, parentId: number, name: string, sku: string }` | +| `getBySku` | `(googleAuthId: string, sku: string)` | `ArticleDetail` (see below) | +| `updateBySku` | `(googleAuthId: string, sku: string, { name?: string, unitamount?: number, unitSi?: string })` | `ArticleDetail` | +| `setPricesBySku` | `(googleAuthId: string, sku: string, PriceInput[])` | `ArticleDetail` | +| `setEansBySku` | `(googleAuthId: string, sku: string, string[])` | `ArticleDetail` | +| `setBrandIdBySku` | `(googleAuthId: string, sku: string, number \| null)` | `ArticleDetail` | +| `setWeightBySku` | `(googleAuthId: string, sku: string, number)` | `ArticleDetail` | +| `setCategoryIdsBySku` | `(googleAuthId: string, sku: string, number[])` | `ArticleDetail` | +| `addImage` | `(googleAuthId: string, sku: string, Buffer)` | `ImageResult` (see below) | +| `deleteImage` | `(googleAuthId: string, sku: string, fullHash: string)` | `void` | +| `orderImages` | `(googleAuthId: string, sku: string, { fullHashes: string[] })` | `{ fullHashes: string[] }` | +| `delete` | `(googleAuthId: string, sku: string)` | `void` | `addImage` accepts a full-size AVIF `Buffer`. Full image: if already AVIF and ≤1000×1000, stored as-is; otherwise scaled to max 1000×1000 (`pic.fullbinary`). Thumb always scaled to max 200×200 (`pic.thumbbinary`) via `sharp`. Assigns random `fullHash` (`pic.hash`, unique), computes `thumbhash`, links `articlepic`. Auth like `getBySku`. Errors: `Article not found`, `Not authorized`. -`addImage` returns: +`addImage` returns `ImageResult`: -```js +```ts { - picId, - sort, - full: { w, h, size }, // AVIF, max 1000×1000 (stored as-is when already AVIF and within bounds) - thumb: { w, h }, // scaled AVIF, max 200×200 (fit inside) - fullHash, // random 64-char hex id (pic.hash, unique) - thumbHash, // base64 thumbhash of thumb preview + picId: number, + sort: number, + full: { w: number, h: number, size: number }, // AVIF, max 1000×1000 + thumb: { w: number, h: number }, // scaled AVIF, max 200×200 + fullHash: string, // random 64-char hex, unique + thumbHash: string, // base64 thumbhash of thumb } ``` -`getBySku` / `updateBySku` / `setPricesBySku` return: +`listSearchIndex` returns every article in the company (reachable via the category tree). Designed for client-side search / autocomplete preload. Auth via `resolveCompanyAccess`. Errors: `Company not found`, `Not authorized`. -```js -{ - id, sku, name, slug, - unitamount, weight, - unit: { si, basesi, basesifactor, symbol, name }, - brand: { name, thumbW, thumbH, thumbHash } | null, - categoryIds: number[], - photos: { sort, fullHash, thumbW, thumbH, thumbHash }[], - eans: string[], - prices: { minmultiplier, net, taxRate, customergroupId }[], - optionsCount, articleListCount, childCount, - fatherArticleId, fatherSku, +```ts +type SearchIndexEntry = { + id: number, + sku: string, + name: string | null, // null when articletext row is missing + eans: string[], // all EANs, empty array when none + categoryIds: number[], // all category ids, at least one } ``` -`getCategoryArticles` / `getTreeForCompany().articles[]` keep the flat list shape (`net`, `taxRate`, single `fullHash`/`thumbW`/`thumbH`/`thumbHash`). +`getBySku` / `updateBySku` / `setPricesBySku` / `setEansBySku` / `setBrandIdBySku` / `setWeightBySku` / `setCategoryIdsBySku` all return `ArticleDetail`: -`listSearchIndex` returns every article in the company (reachable via the category tree) with `id`, `sku`, `name`, `eans`, and `categoryIds` (all EANs as a flat string array). Designed for client-side search / autocomplete preload. Auth via `resolveCompanyAccess`. Errors: `Company not found`, `Not authorized`. +```ts +type ArticleDetail = { + id: number, + sku: string, + name: string | null, // null when articletext row is missing + slug: string | null, + unitamount: number, + weight: number, + unit: { + si: string, // e.g. "pc" + basesi: string, // e.g. "pc" + basesifactor: number, // e.g. 1 + symbol: string | null, // e.g. "Stk", null when unittext missing + name: string | null, // e.g. "Stück", null when unittext missing + }, + brand: { + name: string, + thumbW: number, + thumbH: number, + thumbHash: string, + } | null, // null when no brand assigned + categoryIds: number[], // at least one + photos: { + sort: number, + fullHash: string, + thumbW: number, + thumbH: number, + thumbHash: string, + }[], // empty array when no photos + eans: string[], // empty array when none + prices: PriceEntry[], // empty array when none + optionsCount: number, // 0 when no optionslist + articleListCount: number, // 0 when no articlelist + childCount: number, // 0 when no children + fatherArticleId: number | null, // null when root article + fatherSku: string | null, // null when root article +} + +type PriceEntry = { + minmultiplier: number, + net: number, + taxRate: number, + customergroupId: number, +} +``` + +`getCategoryArticles` / `getTreeForCompany().articles[]` return a flat list shape (`net`, `taxRate`, single `fullHash`/`thumbW`/`thumbH`/`thumbHash`). `parentId` is the category id. Creates `article` + `articletext` (company default language) + `categoryarticle` in one transaction. Allocates `sku` via `getnextnumberwithprefixandpadding` (`type` `sku`; creates `numbers` row with empty prefix if missing). Default unit `pc` is ensured if missing (`unitamount` 1, `weight` 0). Auth via `resolveCategoryAccess(googleAuthId, parentId)` — `Parent not found` / `Not authorized`. Auth via the article's `categoryarticle` category + `resolveCategoryAccess`. Errors: `Article not found`, `Not authorized`. -`updateBySku` patches article fields in a transaction. At least one of `name`, `unitamount`, `unitSi` required. `name` → `articletext` (slugified `slug`); empty name → `Name required`. `unitSi` → existing `unit.si` lookup (no silent ensure); unknown → `Unit not found`. `unitamount` must be finite and `> 0` → `Invalid unitamount`. Returns the same object as `getBySku`. Errors: `Article not found`, `Not authorized`, `Name required`, `Unit not found`, `Invalid unitamount`. +`updateBySku` patches article fields in a transaction. At least one of `name`, `unitamount`, `unitSi` required. `name` → `articletext` (slugified `slug`); empty name → `Name required`. `unitSi` → existing `unit.si` lookup (no silent ensure); unknown → `Unit not found`. `unitamount` must be finite and `> 0` → `Invalid unitamount`. Returns `ArticleDetail`. Errors: `Article not found`, `Not authorized`, `Name required`, `Unit not found`, `Invalid unitamount`. -`setPricesBySku` replaces all `price` rows for the article’s company `poscustomergroup_id` in one transaction. Each entry: `price` → `net`, `tax` → tax rate (ensured via `tax.ensure`), `minmultiplier` defaults to `1`. Empty array clears all POS prices. Returns the same object as `getBySku`. Errors: `Article not found`, `Not authorized`, `POS customer group required`, `Invalid prices`. +`setPricesBySku` replaces all `price` rows for the article’s company `poscustomergroup_id` in one transaction. Each entry: `price` → `net`, `tax` → tax rate (ensured via `tax.ensure`), `minmultiplier` defaults to `1`. Empty array clears all POS prices. Returns `ArticleDetail`. Errors: `Article not found`, `Not authorized`, `POS customer group required`, `Invalid prices`. -`setEansBySku` replaces all `articleean` rows for the article in one transaction (`replace-all` semantics). EANs must be non-empty unique strings; empty array clears all EANs. Returns the same object as `getBySku` (whose `eans` reflects the new list). Errors: `Article not found`, `Not authorized`, `Invalid eans`. +`setEansBySku` replaces all `articleean` rows for the article in one transaction (`replace-all` semantics). EANs must be non-empty unique strings; empty array clears all EANs. Returns `ArticleDetail` (whose `eans` reflects the new list). Errors: `Article not found`, `Not authorized`, `Invalid eans`. `setBrandIdBySku` sets the article’s `brand_id`. Pass `null` to clear the brand. Requires the brand to belong to the article’s company (else `Brand not found`). Non-null must be a finite id. Errors: `Article not found`, `Not authorized`, `Brand not found`. `setWeightBySku` sets the article’s `weight`. Must be finite and `>= 0` (else `Invalid weight`). Errors: `Article not found`, `Not authorized`, `Invalid weight`. -`setCategoryIdsBySku` replaces the article’s category list (`categoryarticle`) in one transaction. Each category must belong to the article’s company and ids must be unique (else `Invalid categoryIds`); empty array is rejected — an article must have at least one category (else `Article must have at least one category`). Returns the same object as `getBySku` (whose `categoryIds` reflects the new list). Errors: `Article not found`, `Not authorized`, `Invalid categoryIds`, `Article must have at least one category`. +`setCategoryIdsBySku` replaces the article’s category list (`categoryarticle`) in one transaction. Each category must belong to the article’s company and ids must be unique (else `Invalid categoryIds`); empty array is rejected — an article must have at least one category (else `Article must have at least one category`). Returns `ArticleDetail` (whose `categoryIds` reflects the new list). Errors: `Article not found`, `Not authorized`, `Invalid categoryIds`, `Article must have at least one category`. `deleteImage` removes `articlepic` and the `pic` row for this article (`fullHash`). Errors: `Article not found`, `Image not found`, `Not authorized`.