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`.