Rewrite store JSDoc, split quote/order into modules, and generate typed API docs
- Rewrite JSDoc across all store methods to document the actual async methods (real params, returns, throws) instead of the internal wrappers - Split quote/order into individual module files backed by shared manifestDoc helpers; remove the createStore factory and thin wrappers - Deduplicate manifest line helpers into src/store/manifestLines.js - Extend generate-api-doc.js to render parameter/return/throws types and emit per-namespace API.<module>.md files with an API.md index - Regenerate all API docs
This commit is contained in:
@@ -29,7 +29,9 @@ Wenn ein Vertrag unklar oder fehlt: **nicht weiterwursteln**. Human fragen, Vors
|
||||
|
||||
| Repo | Pfad | Inhalt |
|
||||
|------|------|--------|
|
||||
| **dieses** | `README.md` | Store-Methoden, Signaturen, Returns — Vertrag für dashback |
|
||||
| **dieses** | `README.md` | Store-Übersicht, Konventionen, HTTP-Mapping — Vertrag für dashback |
|
||||
| **dieses** | `API.md` | Store-API-Index (TOC → `API.<modul>.md`) |
|
||||
| **dieses** | `API.<modul>.md` | pro Namespace: Methoden, Signaturen, JSDoc-Beispiele (`npm run api-doc`) |
|
||||
| **dieses** | `src/schema.js`, `src/store/` | Schema + Implementierung |
|
||||
| dashback | `../dashback/docs/api/README.md` | HTTP-Routen-Index |
|
||||
| dashback | `../dashback/docs/api/routes/` | eine Markdown-Datei pro Endpoint |
|
||||
@@ -38,6 +40,6 @@ Wenn ein Vertrag unklar oder fehlt: **nicht weiterwursteln**. Human fragen, Vors
|
||||
|
||||
## Was der Human / dashback von dir erwarten
|
||||
|
||||
1. **README.md aktuell halten**, wenn Store-Methoden oder Returns sich ändern — dashback baut Services darauf.
|
||||
1. **README.md** und **`API.md` / `API.<modul>.md`** aktuell halten, wenn Store-Methoden oder Returns sich ändern (`npm run api-doc`) — dashback baut Services darauf.
|
||||
2. Schema-Änderungen so, dass `npm run reset-db` den Zielzustand abbildet; Live-DB nur manuell per `sqlcmd` (Human).
|
||||
3. Bei API-Bedarf aus dem Frontend: dashback-Docs/Logs lesen; fehlende HTTP-Schicht dem Human melden, nicht in dashback implementieren.
|
||||
|
||||
300
API.article.md
300
API.article.md
@@ -8,9 +8,22 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
Adds an image to an article by its SKU, processing it into AVIF variants.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `sku` | `string` | The article SKU. |
|
||||
| `image` | `Buffer` | The source image buffer. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{picId: number, sort: number, full: {w: number, h: number, size: number}, thumb: {w: number, h: number}, fullHash: string, thumbHash: string}>` — The added image info.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = addImage(pool);
|
||||
const result = await fn(42, 'SKU-1', imageBuffer);
|
||||
const result = await store.article.addImage(42, 'SKU-1', imageBuffer);
|
||||
// => { picId, sort, full: { w, h, size }, thumb: { w, h }, fullHash, thumbHash }
|
||||
```
|
||||
|
||||
@@ -20,9 +33,23 @@ const result = await fn(42, 'SKU-1', imageBuffer);
|
||||
|
||||
Creates a new article under a given parent category, allocating a SKU and a default unit.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `input` | `object` | The article creation input. |
|
||||
| `input.googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `input.parentId` | `number` | The id of the parent category. |
|
||||
| `input.name` | `string` | The article name. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, parentId: number, name: string, sku: string}>` — The created article.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = createUnderParent(pool);
|
||||
const article = await fn({ googleAuthId: 42, parentId: 5, name: 'Cola' });
|
||||
const article = await store.article.createUnderParent({ googleAuthId: 42, parentId: 5, name: 'Cola' });
|
||||
// => { id, parentId, name, sku }
|
||||
```
|
||||
|
||||
@@ -32,9 +59,25 @@ const article = await fn({ googleAuthId: 42, parentId: 5, name: 'Cola' });
|
||||
|
||||
Deletes an article by its SKU, refusing when it still has children.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `sku` | `string` | The article SKU. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the article has children or is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = del(pool);
|
||||
await fn(42, 'SKU-1');
|
||||
await store.article.delete(42, 'SKU-1');
|
||||
```
|
||||
|
||||
## `article.deleteImage`
|
||||
@@ -43,9 +86,26 @@ await fn(42, 'SKU-1');
|
||||
|
||||
Deletes an image from an article by its SKU and full image hash.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `sku` | `string` | The article SKU. |
|
||||
| `fullHash` | `string` | The full image hash to delete. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the image is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = deleteImage(pool);
|
||||
await fn(42, 'SKU-1', 'abc123');
|
||||
await store.article.deleteImage(42, 'SKU-1', 'abc123');
|
||||
```
|
||||
|
||||
## `article.getBySku`
|
||||
@@ -54,9 +114,25 @@ await fn(42, 'SKU-1', 'abc123');
|
||||
|
||||
Returns the full detail of an article by its SKU, including unit, brand, categories, photos, EANs and prices.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `sku` | `string` | The article SKU. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{article: object}>` — The full article detail.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the article is not found or the user is not authorized.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = getBySku(pool);
|
||||
const article = await fn(42, 'SKU-1');
|
||||
const article = await store.article.getBySku(42, 'SKU-1');
|
||||
// => { article: { id, sku, name, unit, brand, categoryIds, photos, eans, prices, ... } }
|
||||
```
|
||||
|
||||
@@ -66,9 +142,21 @@ const article = await fn(42, 'SKU-1');
|
||||
|
||||
Returns the change history of an article by its SKU.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `sku` | `string` | The article SKU. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, companyuserId: number|null, date: string, text: string}>>` — The article history entries.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = getHistoryBySku(pool);
|
||||
const history = await fn(42, 'SKU-1');
|
||||
const history = await store.article.getHistoryBySku(42, 'SKU-1');
|
||||
// => [{ id, companyuserId, date, text }]
|
||||
```
|
||||
|
||||
@@ -78,9 +166,25 @@ const history = await fn(42, 'SKU-1');
|
||||
|
||||
Returns articles for export, optionally scoped to a category, including category breadcrumbs and image hashes.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `[categoryId]` | `number|null` | Optional category id to scope the export to; defaults to the company root. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<object>>` — The articles for export.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the category is not reachable from the selected company.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = listExport(pool);
|
||||
const articles = await fn(42, null);
|
||||
const articles = await store.article.listExport(42, null);
|
||||
// => [{ id, sku, name, slug, unitSi, net, taxRate, eans, categoryBreadcrumbs, fullHashes, ... }]
|
||||
```
|
||||
|
||||
@@ -88,11 +192,22 @@ const articles = await fn(42, null);
|
||||
|
||||
*Source: `src/store/article/listSearchIndex.js`*
|
||||
|
||||
Returns a lightweight search index of all articles for a company.
|
||||
Returns a lightweight search index of all articles for the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, sku: string, name: string, eans: Array<string>, categoryIds: Array<number>}>>` — The article search index.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = listSearchIndex(pool);
|
||||
const articles = await fn(42);
|
||||
const articles = await store.article.listSearchIndex(42);
|
||||
// => [{ id, sku, name, eans, categoryIds }]
|
||||
```
|
||||
|
||||
@@ -102,9 +217,27 @@ const articles = await fn(42);
|
||||
|
||||
Reorders the images of an article by its SKU.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `sku` | `string` | The article SKU. |
|
||||
| `input` | `object` | The reorder input. |
|
||||
| `input.fullHashes` | `Array<string>` | The full image hashes in their new order. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{fullHashes: Array<string>}>` — The applied order.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the order is invalid.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = orderImages(pool);
|
||||
const result = await fn(42, 'SKU-1', { fullHashes: ['a', 'b'] });
|
||||
const result = await store.article.orderImages(42, 'SKU-1', { fullHashes: ['a', 'b'] });
|
||||
// => { fullHashes: ['a', 'b'] }
|
||||
```
|
||||
|
||||
@@ -114,9 +247,26 @@ const result = await fn(42, 'SKU-1', { fullHashes: ['a', 'b'] });
|
||||
|
||||
Sets (or clears) the brand of an article by its SKU.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `sku` | `string` | The article SKU. |
|
||||
| `brandId` | `number|null` | The brand id, or `null` to clear the brand. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<object>` — The updated article.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the brand or article is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = setBrandIdBySku(pool);
|
||||
const article = await fn(42, 'SKU-1', 3);
|
||||
const article = await store.article.setBrandIdBySku(42, 'SKU-1', 3);
|
||||
// => { id, sku, name, ... }
|
||||
```
|
||||
|
||||
@@ -126,9 +276,26 @@ const article = await fn(42, 'SKU-1', 3);
|
||||
|
||||
Replaces the category membership of an article by its SKU.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `sku` | `string` | The article SKU. |
|
||||
| `categoryIds` | `Array<number>` | The category ids the article should belong to (at least one). |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<object>` — The updated article.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the category ids are invalid or the article is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = setCategoryIdsBySku(pool);
|
||||
const article = await fn(42, 'SKU-1', [5, 9]);
|
||||
const article = await store.article.setCategoryIdsBySku(42, 'SKU-1', [5, 9]);
|
||||
// => { id, sku, name, ... }
|
||||
```
|
||||
|
||||
@@ -138,9 +305,26 @@ const article = await fn(42, 'SKU-1', [5, 9]);
|
||||
|
||||
Replaces the EAN list of an article by its SKU.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `sku` | `string` | The article SKU. |
|
||||
| `eans` | `Array<string>` | The EANs to set (must be unique, non-empty strings). |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<object>` — The updated article.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the EANs are invalid or the article is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = setEansBySku(pool);
|
||||
const article = await fn(42, 'SKU-1', ['4001234567890']);
|
||||
const article = await store.article.setEansBySku(42, 'SKU-1', ['4001234567890']);
|
||||
// => { id, sku, name, ... }
|
||||
```
|
||||
|
||||
@@ -150,9 +334,26 @@ const article = await fn(42, 'SKU-1', ['4001234567890']);
|
||||
|
||||
Replaces the price list of an article by its SKU.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `sku` | `string` | The article SKU. |
|
||||
| `prices` | `Array<{price: number, tax: number, minmultiplier?: number}>` | The price entries (minmultiplier defaults to 1). |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<object>` — The updated article.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the prices are invalid or the article is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = setPricesBySku(pool);
|
||||
const article = await fn(42, 'SKU-1', [{ price: 1.5, tax: 19, minmultiplier: 1 }]);
|
||||
const article = await store.article.setPricesBySku(42, 'SKU-1', [{ price: 1.5, tax: 19, minmultiplier: 1 }]);
|
||||
// => { id, sku, name, ... }
|
||||
```
|
||||
|
||||
@@ -162,9 +363,26 @@ const article = await fn(42, 'SKU-1', [{ price: 1.5, tax: 19, minmultiplier: 1 }
|
||||
|
||||
Sets the weight of an article by its SKU.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `sku` | `string` | The article SKU. |
|
||||
| `weight` | `number` | The weight (must be >= 0). |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<object>` — The updated article.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the weight is invalid or the article is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = setWeightBySku(pool);
|
||||
const article = await fn(42, 'SKU-1', 1.5);
|
||||
const article = await store.article.setWeightBySku(42, 'SKU-1', 1.5);
|
||||
// => { id, sku, name, ... }
|
||||
```
|
||||
|
||||
@@ -174,8 +392,28 @@ const article = await fn(42, 'SKU-1', 1.5);
|
||||
|
||||
Updates an article's name and/or unit (SI and amount), recording history.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `sku` | `string` | The article SKU. |
|
||||
| `patch` | `object` | The update patch. |
|
||||
| `[patch.name]` | `string` | The new article name. |
|
||||
| `[patch.unitSi]` | `string` | The new unit SI code. |
|
||||
| `[patch.unitamount]` | `number` | The new unit amount (must be > 0). |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<object>` — The updated article.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If no valid field is provided or the article is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = updateBySku(pool);
|
||||
const article = await fn(42, 'SKU-1', { name: 'Cola 1L', unitSi: 'l', unitamount: 1 });
|
||||
const article = await store.article.updateBySku(42, 'SKU-1', { name: 'Cola 1L', unitSi: 'l', unitamount: 1 });
|
||||
// => { id, sku, name, ... }
|
||||
```
|
||||
|
||||
63
API.brand.md
63
API.brand.md
@@ -6,22 +6,49 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
*Source: `src/store/brand/delete.js`*
|
||||
|
||||
Deletes a brand for a company by its id.
|
||||
Deletes a brand for the user's selected company by its id.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the brand to delete. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the brand is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = del(pool);
|
||||
await fn(42, 3);
|
||||
await store.brand.delete(42, 3);
|
||||
```
|
||||
|
||||
## `brand.list`
|
||||
|
||||
*Source: `src/store/brand/list.js`*
|
||||
|
||||
Lists the brands of a company.
|
||||
Lists the brands of the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, name: string, gsprpinfo: string|null}>>` — The brands.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = list(pool);
|
||||
const brands = await fn(42);
|
||||
const brands = await store.brand.list(42);
|
||||
// => [{ id, name, gsprpinfo }]
|
||||
```
|
||||
|
||||
@@ -29,10 +56,28 @@ const brands = await fn(42);
|
||||
|
||||
*Source: `src/store/brand/upsert.js`*
|
||||
|
||||
Creates or updates a brand for a company, keyed by name.
|
||||
Creates or updates a brand for the user's selected company, keyed by name.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `input` | `object` | The brand input. |
|
||||
| `input.name` | `string` | The brand name. |
|
||||
| `[input.gsprpinfo]` | `string` | Optional GSPRP info. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, name: string, gsprpinfo: string|null}>` — The upserted brand.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the brand is invalid.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = upsert(pool);
|
||||
const brand = await fn(42, { name: 'Coca-Cola', gsprpinfo: 'x' });
|
||||
const brand = await store.brand.upsert(42, { name: 'Coca-Cola', gsprpinfo: 'x' });
|
||||
// => { id, name, gsprpinfo }
|
||||
```
|
||||
|
||||
177
API.category.md
177
API.category.md
@@ -8,9 +8,22 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
Adds an image to a category, processing it into AVIF full and thumb variants.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `categoryId` | `number` | The id of the category. |
|
||||
| `image` | `Buffer` | The source image buffer. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>` — The added image info.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = addImage(pool);
|
||||
const result = await fn(42, 5, imageBuffer);
|
||||
const result = await store.category.addImage(42, 5, imageBuffer);
|
||||
// => { id, fullHash, thumbW, thumbH, thumbHash }
|
||||
```
|
||||
|
||||
@@ -20,9 +33,23 @@ const result = await fn(42, 5, imageBuffer);
|
||||
|
||||
Creates a new category under a given parent category.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `input` | `object` | The category creation input. |
|
||||
| `input.googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `input.parentId` | `number` | The id of the parent category. |
|
||||
| `input.name` | `string` | The category name. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, parentId: number, sort: number, name: string, slug: string}>` — The created category.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = createUnderParent(pool);
|
||||
const category = await fn({ googleAuthId: 42, parentId: 5, name: 'Drinks' });
|
||||
const category = await store.category.createUnderParent({ googleAuthId: 42, parentId: 5, name: 'Drinks' });
|
||||
// => { id, parentId, sort, name, slug }
|
||||
```
|
||||
|
||||
@@ -32,9 +59,26 @@ const category = await fn({ googleAuthId: 42, parentId: 5, name: 'Drinks' });
|
||||
|
||||
Deletes a category, refusing to delete the company's root category.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `input` | `object` | The delete input. |
|
||||
| `input.googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `input.id` | `number` | The id of the category to delete. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the category is the root category.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = del(pool);
|
||||
await fn({ googleAuthId: 42, id: 5 });
|
||||
await store.category.delete({ googleAuthId: 42, id: 5 });
|
||||
```
|
||||
|
||||
## `category.deleteImage`
|
||||
@@ -43,10 +87,26 @@ await fn({ googleAuthId: 42, id: 5 });
|
||||
|
||||
Deletes the image of a category.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `categoryId` | `number` | The id of the category. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, thumbW: null, thumbH: null, thumbHash: null}>` — The cleared image info.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the image is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = deleteImage(pool);
|
||||
const result = await fn(42, 5);
|
||||
// => { id, thumbW: null, thumbH: null, thumbHash: null }
|
||||
const result = await store.category.deleteImage(42, 5);
|
||||
// => { id: 5, thumbW: null, thumbH: null, thumbHash: null }
|
||||
```
|
||||
|
||||
## `category.getCategoryArticles`
|
||||
@@ -55,9 +115,21 @@ const result = await fn(42, 5);
|
||||
|
||||
Returns the articles belonging to a single category.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `categoryId` | `number` | The id of the category. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<object>>` — The articles in the category.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = getCategoryArticles(pool);
|
||||
const articles = await fn(42, 5);
|
||||
const articles = await store.category.getCategoryArticles(42, 5);
|
||||
// => [{ id, sku, name, ... }]
|
||||
```
|
||||
|
||||
@@ -65,11 +137,22 @@ const articles = await fn(42, 5);
|
||||
|
||||
*Source: `src/store/category/getTreeForCompany.js`*
|
||||
|
||||
Returns the full category tree for a 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 its articles and the posterminal ids assigned to each category.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{languageId: number, tree: Array<object>, articles: Array<object>}>` — The category tree, articles and language.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = getTreeForCompany(pool);
|
||||
const tree = await fn(42);
|
||||
const tree = await store.category.getTreeForCompany(42);
|
||||
// => { languageId, tree: [{ id, posterminalIds, ... }], articles: [...] }
|
||||
```
|
||||
|
||||
@@ -79,9 +162,27 @@ const tree = await fn(42);
|
||||
|
||||
Moves a category to a new parent, preventing cycles and cross-company moves.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `input` | `object` | The move input. |
|
||||
| `input.googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `input.id` | `number` | The id of the category to move. |
|
||||
| `input.parentId` | `number` | The id of the new parent category. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, parentId: number, sort: number}>` — The moved category.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the move would create a cycle or cross companies.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = move(pool);
|
||||
const result = await fn({ googleAuthId: 42, id: 5, parentId: 9 });
|
||||
const result = await store.category.move({ googleAuthId: 42, id: 5, parentId: 9 });
|
||||
// => { id, parentId, sort }
|
||||
```
|
||||
|
||||
@@ -91,9 +192,27 @@ const result = await fn({ googleAuthId: 42, id: 5, parentId: 9 });
|
||||
|
||||
Reorders the child categories of a given parent.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `input` | `object` | The reorder input. |
|
||||
| `input.googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `input.parentId` | `number` | The id of the parent category. |
|
||||
| `input.categoryIds` | `Array<number>` | The child category ids in their new order. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{categoryIds: Array<number>}>` — The applied order.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the order is invalid.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = order(pool);
|
||||
const result = await fn({ googleAuthId: 42, parentId: 5, categoryIds: [3, 1, 2] });
|
||||
const result = await store.category.order({ googleAuthId: 42, parentId: 5, categoryIds: [3, 1, 2] });
|
||||
// => { categoryIds: [3, 1, 2] }
|
||||
```
|
||||
|
||||
@@ -103,8 +222,26 @@ const result = await fn({ googleAuthId: 42, parentId: 5, categoryIds: [3, 1, 2]
|
||||
|
||||
Renames a category and updates its slug.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `input` | `object` | The rename input. |
|
||||
| `input.googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `input.id` | `number` | The id of the category to rename. |
|
||||
| `input.name` | `string` | The new category name. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, name: string, slug: string}>` — The renamed category.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the name is empty or the category is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = rename(pool);
|
||||
const result = await fn({ googleAuthId: 42, id: 5, name: 'Soft Drinks' });
|
||||
const result = await store.category.rename({ googleAuthId: 42, id: 5, name: 'Soft Drinks' });
|
||||
// => { id, name, slug }
|
||||
```
|
||||
|
||||
@@ -8,6 +8,27 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
Creates a new company together with its root category, POS customer group, default language and tax rates, and links it to the given Google auth user.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `input` | `object` | The company creation input. |
|
||||
| `input.iso639` | `string` | The ISO 639 language code for the company. |
|
||||
| `input.name` | `string` | The company name (must be unique). |
|
||||
| `input.taxrate` | `number` | The standard tax rate in percent. |
|
||||
| `input.reducedtaxrate` | `number` | The reduced tax rate in percent. |
|
||||
| `input.googleAuthId` | `number` | The id of the Google auth user to link. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, rootCategoryId: number, languageId: number, taxrateId: number, reducedtaxrateId: number, taxfreerateId: number, posCustomerGroupId: number}>` — The created company ids.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the company name is already taken.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const company = await store.company.create({
|
||||
iso639: 'de',
|
||||
@@ -25,6 +46,18 @@ const company = await store.company.create({
|
||||
|
||||
Deletes a company by its id.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `companyId` | `number` | The id of the company to delete. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
await store.company.delete(1);
|
||||
```
|
||||
@@ -35,6 +68,19 @@ await store.company.delete(1);
|
||||
|
||||
Returns a single company by its id.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | `number` | The id of the company. |
|
||||
| `[tx]` | `object` | Optional transaction to run within. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<object|undefined>` — The company row, or `undefined` if not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const company = await store.company.getById(1);
|
||||
// => { id, name, ... }
|
||||
@@ -46,6 +92,19 @@ const company = await store.company.getById(1);
|
||||
|
||||
Returns a single company by its name.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `name` | `string` | The company name. |
|
||||
| `[tx]` | `object` | Optional transaction to run within. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<object|undefined>` — The company row, or `undefined` if not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const company = await store.company.getByName('Acme');
|
||||
// => { id, name, ... }
|
||||
@@ -57,6 +116,18 @@ const company = await store.company.getByName('Acme');
|
||||
|
||||
Lists the companies accessible to the given Google auth user.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<object>>` — The accessible company rows.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const companies = await store.company.list(42);
|
||||
// => [{ id, name, ... }]
|
||||
@@ -68,6 +139,27 @@ const companies = await store.company.list(42);
|
||||
|
||||
Updates an existing company's name, language and tax rates.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | `number` | The id of the company to update. |
|
||||
| `input` | `object` | The update input. |
|
||||
| `input.iso639` | `string` | The ISO 639 language code. |
|
||||
| `input.name` | `string` | The new company name (must be unique). |
|
||||
| `input.taxrate` | `number` | The standard tax rate in percent. |
|
||||
| `input.reducedtaxrate` | `number` | The reduced tax rate in percent. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, rootCategoryId: number, languageId: number, taxrateId: number, reducedtaxrateId: number, taxfreerateId: number}>` — The updated company ids.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the company is not found or the name is taken.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const result = await store.company.update(1, { iso639: 'de', name: 'Acme', taxrate: 19, reducedtaxrate: 7 });
|
||||
// => { id, rootCategoryId, languageId, taxrateId, reducedtaxrateId, taxfreerateId }
|
||||
|
||||
@@ -6,22 +6,52 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
*Source: `src/store/companysetting/delete.js`*
|
||||
|
||||
Deletes a company setting by key.
|
||||
Deletes a company setting by key for the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `key` | `string` | The setting key. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the setting is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = del(pool);
|
||||
await fn(42, 'theme');
|
||||
await store.companysetting.delete(42, 'theme');
|
||||
```
|
||||
|
||||
## `companysetting.get`
|
||||
|
||||
*Source: `src/store/companysetting/get.js`*
|
||||
|
||||
Returns a single company setting by key.
|
||||
Returns a single company setting by key for the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `key` | `string` | The setting key. |
|
||||
| `[options]` | `object` | Optional options. |
|
||||
| `[options.frontendOnly]` | `boolean` | Only return the setting if it is frontend-visible. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{key: string, value: string, frontend: boolean}|undefined>` — The setting, or `undefined` if not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = get(pool);
|
||||
const setting = await fn(42, 'theme');
|
||||
const setting = await store.companysetting.get(42, 'theme');
|
||||
// => { key, value, frontend }
|
||||
```
|
||||
|
||||
@@ -29,11 +59,24 @@ const setting = await fn(42, 'theme');
|
||||
|
||||
*Source: `src/store/companysetting/list.js`*
|
||||
|
||||
Lists the settings of a company, optionally only frontend-visible ones.
|
||||
Lists the settings of the user's selected company, optionally only frontend-visible ones.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `[options]` | `object` | Optional options. |
|
||||
| `[options.frontendOnly]` | `boolean` | Only return frontend-visible settings. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{key: string, value: string, frontend: boolean}>>` — The settings.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = list(pool);
|
||||
const settings = await fn(42, { frontendOnly: true });
|
||||
const settings = await store.companysetting.list(42, { frontendOnly: true });
|
||||
// => [{ key, value, frontend }]
|
||||
```
|
||||
|
||||
@@ -41,10 +84,29 @@ const settings = await fn(42, { frontendOnly: true });
|
||||
|
||||
*Source: `src/store/companysetting/set.js`*
|
||||
|
||||
Sets (upserts) a company setting by key.
|
||||
Sets (upserts) a company setting by key for the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `input` | `object` | The setting input. |
|
||||
| `input.key` | `string` | The setting key. |
|
||||
| `input.value` | `string` | The setting value. |
|
||||
| `[input.frontend]` | `boolean` | Whether the setting is frontend-visible. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{key: string, value: string, frontend: boolean}>` — The upserted setting.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the input is invalid.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = set(pool);
|
||||
const setting = await fn(42, { key: 'theme', value: 'dark', frontend: true });
|
||||
const setting = await store.companysetting.set(42, { key: 'theme', value: 'dark', frontend: true });
|
||||
// => { key, value, frontend }
|
||||
```
|
||||
|
||||
@@ -8,6 +8,19 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
Adds a company user linking a company to a Google auth account.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `companyId` | `number` | The id of the company. |
|
||||
| `googleAuthId` | `number` | The id of the Google auth account. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<number>` — The id of the created company user link.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const id = await store.companyuser.add(1, 42);
|
||||
// => 7
|
||||
@@ -19,6 +32,18 @@ const id = await store.companyuser.add(1, 42);
|
||||
|
||||
Deletes a company user link by its id.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | `number` | The id of the company user link to delete. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
await store.companyuser.delete(7);
|
||||
```
|
||||
@@ -29,6 +54,18 @@ await store.companyuser.delete(7);
|
||||
|
||||
Returns the company user ids associated with the given email address.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `email` | `string` | The email address. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<number>>` — The company user ids.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const ids = await store.companyuser.getByEmail('user@example.com');
|
||||
// => [1, 2, 3]
|
||||
|
||||
@@ -6,11 +6,28 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
*Source: `src/store/customergroup/create.js`*
|
||||
|
||||
Creates a new customer group for a company.
|
||||
Creates a new customer group for the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `input` | `object` | The customer group input. |
|
||||
| `input.name` | `string` | The customer group name (must not be a reserved name). |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, name: string, sort: number}>` — The created customer group.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the name is empty or reserved.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = create(pool);
|
||||
const group = await fn(42, { name: 'Wholesale' });
|
||||
const group = await store.customergroup.create(42, { name: 'Wholesale' });
|
||||
// => { id, name, sort }
|
||||
```
|
||||
|
||||
@@ -18,22 +35,49 @@ const group = await fn(42, { name: 'Wholesale' });
|
||||
|
||||
*Source: `src/store/customergroup/delete.js`*
|
||||
|
||||
Deletes a customer group for a company by its id.
|
||||
Deletes a customer group for the user's selected company by its id.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the customer group to delete. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the customer group is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = del(pool);
|
||||
await fn(42, 5);
|
||||
await store.customergroup.delete(42, 5);
|
||||
```
|
||||
|
||||
## `customergroup.list`
|
||||
|
||||
*Source: `src/store/customergroup/list.js`*
|
||||
|
||||
Lists the customer groups of a company.
|
||||
Lists the customer groups of the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, name: string, sort: number}>>` — The customer groups.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = list(pool);
|
||||
const groups = await fn(42);
|
||||
const groups = await store.customergroup.list(42);
|
||||
// => [{ id, name, sort }]
|
||||
```
|
||||
|
||||
@@ -41,11 +85,28 @@ const groups = await fn(42);
|
||||
|
||||
*Source: `src/store/customergroup/order.js`*
|
||||
|
||||
Reorders the customer groups of a company.
|
||||
Reorders the customer groups of the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `input` | `object` | The reorder input. |
|
||||
| `input.customergroupIds` | `Array<number>` | The customer group ids in their new order. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{customergroupIds: Array<number>}>` — The applied order.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the order is invalid.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = order(pool);
|
||||
const result = await fn(42, { customergroupIds: [3, 1, 2] });
|
||||
const result = await store.customergroup.order(42, { customergroupIds: [3, 1, 2] });
|
||||
// => { customergroupIds: [3, 1, 2] }
|
||||
```
|
||||
|
||||
@@ -53,10 +114,28 @@ const result = await fn(42, { customergroupIds: [3, 1, 2] });
|
||||
|
||||
*Source: `src/store/customergroup/update.js`*
|
||||
|
||||
Updates the name of a customer group for a company.
|
||||
Updates the name of a customer group for the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the customer group to update. |
|
||||
| `input` | `object` | The update input. |
|
||||
| `input.name` | `string` | The new customer group name. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, name: string, sort: number}>` — The updated customer group.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the customer group is not found or the name is empty.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = update(pool);
|
||||
const group = await fn(42, 5, { name: 'Retail' });
|
||||
const group = await store.customergroup.update(42, 5, { name: 'Retail' });
|
||||
// => { id, name, sort }
|
||||
```
|
||||
|
||||
112
API.delivery.md
112
API.delivery.md
@@ -6,11 +6,31 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
*Source: `src/store/delivery/create.js`*
|
||||
|
||||
Creates a new delivery for a company with its lines.
|
||||
Creates a new delivery for the user's selected company with its lines.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `body` | `object` | The delivery input. |
|
||||
| `body.number` | `string` | The delivery number. |
|
||||
| `body.date` | `string` | The delivery date. |
|
||||
| `body.supplierId` | `number` | The id of the supplier. |
|
||||
| `[body.lines]` | `Array<object>` | The delivery lines. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, number: string, type: string, entityId: number, date: string, lines: Array<object>}>` — The created delivery.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the delivery is invalid.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = create(pool);
|
||||
const delivery = await fn(42, { number: 'D-1', date: '2026-01-01', supplierId: 5, lines: [...] });
|
||||
const delivery = await store.delivery.create(42, { number: 'D-1', date: '2026-01-01', supplierId: 5, lines: [...] });
|
||||
// => { id, number, type, entityId, date, lines: [...] }
|
||||
```
|
||||
|
||||
@@ -18,22 +38,54 @@ const delivery = await fn(42, { number: 'D-1', date: '2026-01-01', supplierId: 5
|
||||
|
||||
*Source: `src/store/delivery/delete.js`*
|
||||
|
||||
Deletes a delivery by its id within a company.
|
||||
Deletes a delivery by its id within the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the delivery to delete. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the delivery is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = del(pool);
|
||||
await fn(42, 5);
|
||||
await store.delivery.delete(42, 5);
|
||||
```
|
||||
|
||||
## `delivery.getById`
|
||||
|
||||
*Source: `src/store/delivery/getById.js`*
|
||||
|
||||
Returns a single delivery by its id within a company.
|
||||
Returns a single delivery by its id within the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the delivery. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, number: string, type: string, entityId: number, date: string, lines: Array<object>}>` — The delivery.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the delivery is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = getById(pool);
|
||||
const delivery = await fn(42, 5);
|
||||
const delivery = await store.delivery.getById(42, 5);
|
||||
// => { id, number, type, entityId, date, lines: [...] }
|
||||
```
|
||||
|
||||
@@ -41,11 +93,22 @@ const delivery = await fn(42, 5);
|
||||
|
||||
*Source: `src/store/delivery/list.js`*
|
||||
|
||||
Lists the deliveries of a company.
|
||||
Lists the deliveries of the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, number: string, type: string, entityId: number, date: string}>>` — The deliveries.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = list(pool);
|
||||
const deliveries = await fn(42);
|
||||
const deliveries = await store.delivery.list(42);
|
||||
// => [{ id, number, type, entityId, date }]
|
||||
```
|
||||
|
||||
@@ -55,8 +118,29 @@ const deliveries = await fn(42);
|
||||
|
||||
Updates a delivery's header and/or lines.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the delivery to update. |
|
||||
| `patch` | `object` | The update patch. |
|
||||
| `[patch.number]` | `string` | The new delivery number. |
|
||||
| `[patch.date]` | `string` | The new delivery date. |
|
||||
| `[patch.supplierId]` | `number` | The new supplier id. |
|
||||
| `[patch.lines]` | `Array<object>` | The new delivery lines. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, number: string, type: string, entityId: number, date: string, lines: Array<object>}>` — The updated delivery.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the delivery is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = update(pool);
|
||||
const delivery = await fn(42, 5, { number: 'D-2', lines: [...] });
|
||||
const delivery = await store.delivery.update(42, 5, { number: 'D-2', lines: [...] });
|
||||
// => { id, number, type, entityId, date, lines: [...] }
|
||||
```
|
||||
|
||||
159
API.entity.md
159
API.entity.md
@@ -8,9 +8,29 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
Creates a new entity (customer) in a customer group, recording history. Optional `number`; if omitted, allocates via `getnextnumberwithprefixandpadding` (`type` `entity`).
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `input` | `object` | The entity creation input. |
|
||||
| `input.customergroupId` | `number` | The id of the customer group. |
|
||||
| `input.name` | `string` | The entity name. |
|
||||
| `[input.firstName]` | `string` | The entity first name. |
|
||||
| `[input.number]` | `string` | Optional explicit customer number. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, customergroupId: number, firstName: string|null, name: string, number: string, createdAt: string, updatedAt: string}>` — The created entity.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the name is empty.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = create(pool);
|
||||
const entity = await fn(42, { customergroupId: 5, name: 'Doe', firstName: 'John' });
|
||||
const entity = await store.entity.create(42, { customergroupId: 5, name: 'Doe', firstName: 'John' });
|
||||
// => { id, customergroupId, firstName, name, number, createdAt, updatedAt }
|
||||
```
|
||||
|
||||
@@ -18,22 +38,54 @@ const entity = await fn(42, { customergroupId: 5, name: 'Doe', firstName: 'John'
|
||||
|
||||
*Source: `src/store/entity/delete.js`*
|
||||
|
||||
Deletes an entity by its id within a company.
|
||||
Deletes an entity by its id within the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the entity to delete. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the entity is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = del(pool);
|
||||
await fn(42, 5);
|
||||
await store.entity.delete(42, 5);
|
||||
```
|
||||
|
||||
## `entity.getById`
|
||||
|
||||
*Source: `src/store/entity/getById.js`*
|
||||
|
||||
Returns a single entity by its id within a company.
|
||||
Returns a single entity by its id within the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the entity. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, customergroupId: number, firstName: string|null, name: string, number: string, createdAt: string, updatedAt: string}>` — The entity.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the entity is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = getById(pool);
|
||||
const entity = await fn(42, 5);
|
||||
const entity = await store.entity.getById(42, 5);
|
||||
// => { id, customergroupId, firstName, name, number, createdAt, updatedAt }
|
||||
```
|
||||
|
||||
@@ -41,13 +93,31 @@ const entity = await fn(42, 5);
|
||||
|
||||
*Source: `src/store/entity/isNumberFree.js`*
|
||||
|
||||
Returns whether a customer number is free within the company. Pass `excludeEntityId` when editing so the entity's own number counts as free.
|
||||
Returns whether a customer number is free within the user's selected company. Pass `excludeEntityId` when editing so the entity's own number counts as free.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `number` | `string` | The customer number to check. |
|
||||
| `[options]` | `object` | Optional options. |
|
||||
| `[options.excludeEntityId]` | `number` | An entity id to exclude from the check. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<boolean>` — Whether the number is free.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the number is empty.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = isNumberFree(pool);
|
||||
const free = await fn(42, 'C-100');
|
||||
const free = await store.entity.isNumberFree(42, 'C-100');
|
||||
// => true
|
||||
const freeOnEdit = await fn(42, 'C-100', { excludeEntityId: 5 });
|
||||
const freeOnEdit = await store.entity.isNumberFree(42, 'C-100', { excludeEntityId: 5 });
|
||||
// => true
|
||||
```
|
||||
|
||||
@@ -55,11 +125,29 @@ const freeOnEdit = await fn(42, 'C-100', { excludeEntityId: 5 });
|
||||
|
||||
*Source: `src/store/entity/list.js`*
|
||||
|
||||
Lists the entities (customers) of a company, optionally filtered by customer group or restricted to the 'customer' role.
|
||||
Lists the entities (customers) of the user's selected company, optionally filtered by customer group or restricted to the 'customer' role.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `[options]` | `object` | Optional options. |
|
||||
| `[options.customergroupId]` | `number` | Filter by customer group id. |
|
||||
| `[options.role]` | `string` | Restrict to `'customer'` role if set. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, customergroupId: number, firstName: string|null, name: string, number: string, createdAt: string, updatedAt: string}>>` — The entities.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the role is invalid.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = list(pool);
|
||||
const entities = await fn(42, { customergroupId: 5 });
|
||||
const entities = await store.entity.list(42, { customergroupId: 5 });
|
||||
// => [{ id, customergroupId, firstName, name, number, createdAt, updatedAt }]
|
||||
```
|
||||
|
||||
@@ -67,11 +155,22 @@ const entities = await fn(42, { customergroupId: 5 });
|
||||
|
||||
*Source: `src/store/entity/peekNextNumber.js`*
|
||||
|
||||
Returns the next customer number that would be allocated for the company (`numbers` type `entity`) without consuming it.
|
||||
Returns the next customer number that would be allocated for the user's selected company (`numbers` type `entity`) without consuming it.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<string>` — The next entity number.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = peekNextNumber(pool);
|
||||
const number = await fn(42);
|
||||
const number = await store.entity.peekNextNumber(42);
|
||||
// => '1'
|
||||
```
|
||||
|
||||
@@ -81,8 +180,28 @@ const number = await fn(42);
|
||||
|
||||
Updates name, first name, and/or customer number of an entity, recording history. Optional `number` sets the customer number explicitly (does not auto-allocate).
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the entity to update. |
|
||||
| `patch` | `object` | The update patch. |
|
||||
| `[patch.name]` | `string` | The new entity name. |
|
||||
| `[patch.firstName]` | `string` | The new first name. |
|
||||
| `[patch.number]` | `string` | The new customer number. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, customergroupId: number, firstName: string|null, name: string, number: string, createdAt: string, updatedAt: string}>` — The updated entity.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If no valid field is provided or the entity is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = update(pool);
|
||||
const entity = await fn(42, 5, { name: 'Doe', firstName: 'Jane', number: 'C-42' });
|
||||
const entity = await store.entity.update(42, 5, { name: 'Doe', firstName: 'Jane', number: 'C-42' });
|
||||
// => { id, customergroupId, firstName, name, number, createdAt, updatedAt }
|
||||
```
|
||||
|
||||
@@ -8,9 +8,35 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
Creates a new address for an entity.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `entityId` | `number` | The id of the entity. |
|
||||
| `input` | `object` | The address input. |
|
||||
| `input.addressType` | `string` | The address type (e.g. `'billing'`). |
|
||||
| `[input.firstName]` | `string` | The first name. |
|
||||
| `[input.name]` | `string` | The name. |
|
||||
| `[input.street]` | `string` | The street. |
|
||||
| `[input.streetNumber]` | `string` | The street number. |
|
||||
| `[input.zip]` | `string` | The zip code. |
|
||||
| `[input.city]` | `string` | The city. |
|
||||
| `[input.county]` | `string` | The county. |
|
||||
| `[input.sort]` | `number` | Optional sort order. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<object>` — The created address.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the address type is missing.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = create(pool);
|
||||
const address = await fn(42, 5, { addressType: 'billing', street: 'Main St', city: 'Berlin' });
|
||||
const address = await store.entityaddress.create(42, 5, { addressType: 'billing', street: 'Main St', city: 'Berlin' });
|
||||
// => { id, entityId, firstName, name, street, streetNumber, zip, city, county, addressType, sort }
|
||||
```
|
||||
|
||||
@@ -20,9 +46,26 @@ const address = await fn(42, 5, { addressType: 'billing', street: 'Main St', cit
|
||||
|
||||
Deletes an address of an entity.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `entityId` | `number` | The id of the entity. |
|
||||
| `id` | `number` | The id of the address to delete. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the address is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = del(pool);
|
||||
await fn(42, 5, 9);
|
||||
await store.entityaddress.delete(42, 5, 9);
|
||||
```
|
||||
|
||||
## `entityaddress.list`
|
||||
@@ -31,9 +74,21 @@ await fn(42, 5, 9);
|
||||
|
||||
Lists the addresses of an entity.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `entityId` | `number` | The id of the entity. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<object>>` — The addresses.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = list(pool);
|
||||
const addresses = await fn(42, 5);
|
||||
const addresses = await store.entityaddress.list(42, 5);
|
||||
// => [{ id, entityId, firstName, name, street, streetNumber, zip, city, county, addressType, sort }]
|
||||
```
|
||||
|
||||
@@ -43,8 +98,26 @@ const addresses = await fn(42, 5);
|
||||
|
||||
Updates an address of an entity.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `entityId` | `number` | The id of the entity. |
|
||||
| `id` | `number` | The id of the address to update. |
|
||||
| `patch` | `object` | The update patch (any subset of address fields). |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<object>` — The updated address.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the address is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = update(pool);
|
||||
const address = await fn(42, 5, 9, { city: 'Hamburg' });
|
||||
const address = await store.entityaddress.update(42, 5, 9, { city: 'Hamburg' });
|
||||
// => { id, entityId, firstName, name, street, streetNumber, zip, city, county, addressType, sort }
|
||||
```
|
||||
|
||||
@@ -8,9 +8,29 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
Creates a new contact for an entity.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `entityId` | `number` | The id of the entity. |
|
||||
| `input` | `object` | The contact input. |
|
||||
| `input.contactType` | `string` | The contact type (e.g. `'email'`). |
|
||||
| `input.contact` | `string` | The contact value. |
|
||||
| `[input.sort]` | `number` | Optional sort order. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, entityId: number, contactType: string, contact: string, sort: number}>` — The created contact.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the contact type or value is missing.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = create(pool);
|
||||
const contact = await fn(42, 5, { contactType: 'email', contact: 'a@b.c' });
|
||||
const contact = await store.entitycontact.create(42, 5, { contactType: 'email', contact: 'a@b.c' });
|
||||
// => { id, entityId, contactType, contact, sort }
|
||||
```
|
||||
|
||||
@@ -20,9 +40,26 @@ const contact = await fn(42, 5, { contactType: 'email', contact: 'a@b.c' });
|
||||
|
||||
Deletes a contact of an entity.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `entityId` | `number` | The id of the entity. |
|
||||
| `id` | `number` | The id of the contact to delete. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the contact is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = del(pool);
|
||||
await fn(42, 5, 9);
|
||||
await store.entitycontact.delete(42, 5, 9);
|
||||
```
|
||||
|
||||
## `entitycontact.list`
|
||||
@@ -31,9 +68,21 @@ await fn(42, 5, 9);
|
||||
|
||||
Lists the contacts of an entity.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `entityId` | `number` | The id of the entity. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, entityId: number, contactType: string, contact: string, sort: number}>>` — The contacts.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = list(pool);
|
||||
const contacts = await fn(42, 5);
|
||||
const contacts = await store.entitycontact.list(42, 5);
|
||||
// => [{ id, entityId, contactType, contact, sort }]
|
||||
```
|
||||
|
||||
@@ -43,8 +92,26 @@ const contacts = await fn(42, 5);
|
||||
|
||||
Updates a contact of an entity.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `entityId` | `number` | The id of the entity. |
|
||||
| `id` | `number` | The id of the contact to update. |
|
||||
| `patch` | `object` | The update patch (any subset of contact fields). |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, entityId: number, contactType: string, contact: string, sort: number}>` — The updated contact.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the contact is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = update(pool);
|
||||
const contact = await fn(42, 5, 9, { contact: 'b@c.d' });
|
||||
const contact = await store.entitycontact.update(42, 5, 9, { contact: 'b@c.d' });
|
||||
// => { id, entityId, contactType, contact, sort }
|
||||
```
|
||||
|
||||
@@ -8,9 +8,20 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
Adds a new allowed Google auth email.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `email` | `string` | The email address to allow. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<number>` — The id of the created Google auth row.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = add(pool);
|
||||
const id = await fn('user@example.com');
|
||||
const id = await store.google_auth.add('user@example.com');
|
||||
// => 1
|
||||
```
|
||||
|
||||
@@ -20,9 +31,20 @@ const id = await fn('user@example.com');
|
||||
|
||||
Returns a Google auth row by email.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `email` | `string` | The email address. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<object|undefined>` — The Google auth row, or `undefined` if not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = getByEmail(pool);
|
||||
const auth = await fn('user@example.com');
|
||||
const auth = await store.google_auth.getByEmail('user@example.com');
|
||||
// => { id, email, ... }
|
||||
```
|
||||
|
||||
@@ -32,9 +54,20 @@ const auth = await fn('user@example.com');
|
||||
|
||||
Returns a Google auth row by its id.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth row. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<object|undefined>` — The Google auth row, or `undefined` if not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = getById(pool);
|
||||
const auth = await fn(42);
|
||||
const auth = await store.google_auth.getById(42);
|
||||
// => { id, email, ... }
|
||||
```
|
||||
|
||||
@@ -44,9 +77,14 @@ const auth = await fn(42);
|
||||
|
||||
Lists all allowed Google auth emails.
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, email: string}>>` — The allowed emails.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = listAllowedEmails(pool);
|
||||
const emails = await fn();
|
||||
const emails = await store.google_auth.listAllowedEmails();
|
||||
// => [{ id, email }]
|
||||
```
|
||||
|
||||
@@ -56,9 +94,20 @@ const emails = await fn();
|
||||
|
||||
Removes an allowed Google auth email.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `email` | `string` | The email address to remove. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = removeAllowedEmail(pool);
|
||||
await fn('user@example.com');
|
||||
await store.google_auth.removeAllowedEmail('user@example.com');
|
||||
```
|
||||
|
||||
## `google_auth.setProfile`
|
||||
@@ -67,9 +116,28 @@ await fn('user@example.com');
|
||||
|
||||
Updates the profile (picture, picture binary, sub) of a Google auth user.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `input` | `object` | The profile input. |
|
||||
| `[input.picture]` | `string` | The picture URL. |
|
||||
| `[input.pictureBinary]` | `Buffer` | The picture binary. |
|
||||
| `[input.sub]` | `string` | The Google sub identifier. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the user is not found or the picture binary is invalid.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = setProfile(pool);
|
||||
await fn(42, { picture: 'url', pictureBinary: buffer, sub: 'sub' });
|
||||
await store.google_auth.setProfile(42, { picture: 'url', pictureBinary: buffer, sub: 'sub' });
|
||||
```
|
||||
|
||||
## `google_auth.setSelectedCompany`
|
||||
@@ -78,7 +146,19 @@ await fn(42, { picture: 'url', pictureBinary: buffer, sub: 'sub' });
|
||||
|
||||
Sets the currently selected company for a Google auth user.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `companyId` | `number` | The id of the company to select. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = setSelectedCompany(pool);
|
||||
await fn(42, 1);
|
||||
await store.google_auth.setSelectedCompany(42, 1);
|
||||
```
|
||||
|
||||
57
API.lang.md
57
API.lang.md
@@ -8,6 +8,19 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
Creates a new language by its ISO 639 code.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `iso639` | `string` | The ISO 639 language code (e.g. `'de'`). |
|
||||
| `[tx]` | `object` | Optional transaction to run within. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<number>` — The id of the created language.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const id = await store.lang.create('de');
|
||||
// => 1
|
||||
@@ -19,6 +32,18 @@ const id = await store.lang.create('de');
|
||||
|
||||
Deletes a language by its id.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | `number` | The id of the language to delete. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
await store.lang.delete(1);
|
||||
```
|
||||
@@ -29,6 +54,19 @@ await store.lang.delete(1);
|
||||
|
||||
Returns the id of a language by ISO code, creating it if it does not exist.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `iso639` | `string` | The ISO 639 language code (e.g. `'de'`). |
|
||||
| `[tx]` | `object` | Optional transaction to run within. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<number>` — The id of the language.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const id = await store.lang.ensure('de');
|
||||
// => 1
|
||||
@@ -40,6 +78,19 @@ const id = await store.lang.ensure('de');
|
||||
|
||||
Returns a language row by its ISO 639 code.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `iso639` | `string` | The ISO 639 language code (e.g. `'de'`). |
|
||||
| `[tx]` | `object` | Optional transaction to run within. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, iso639: string}>` — The language row, or `undefined` if not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const lang = await store.lang.getByIso639('de');
|
||||
// => { id: 1, iso639: 'de' }
|
||||
@@ -51,6 +102,12 @@ const lang = await store.lang.getByIso639('de');
|
||||
|
||||
Lists all languages.
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, iso639: string}>>` — All language rows.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const languages = await store.lang.list();
|
||||
// => [{ id: 1, iso639: 'de' }]
|
||||
|
||||
2
API.md
2
API.md
@@ -21,6 +21,8 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
|
||||
- [entitycontact](API.entitycontact.md)
|
||||
- [supplier](API.supplier.md)
|
||||
- [delivery](API.delivery.md)
|
||||
- [quote](API.quote.md)
|
||||
- [order](API.order.md)
|
||||
- [posterminal](API.posterminal.md)
|
||||
- [google_auth](API.google_auth.md)
|
||||
- [pic](API.pic.md)
|
||||
|
||||
146
API.order.md
Normal file
146
API.order.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# order
|
||||
|
||||
This document is auto-generated from the JSDoc comments in `src/store` by running `npm run api-doc`.
|
||||
|
||||
## `order.create`
|
||||
|
||||
*Source: `src/store/order/create.js`*
|
||||
|
||||
Creates a new order for the user's selected company with its lines.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `body` | `object` | The order input. |
|
||||
| `body.number` | `string` | The order number. |
|
||||
| `body.date` | `string` | The order date. |
|
||||
| `[body.entityId]` | `number` | The id of the customer entity. |
|
||||
| `[body.lines]` | `Array<object>` | The order lines. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, number: string, type: string, entityId: number|null, date: string, lines: Array<object>}>` — The created order.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the order is invalid.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const order = await store.order.create(42, { number: 'B-2026-001', date: '2026-09-15', entityId: 12, lines: [...] });
|
||||
// => { id, number, type, entityId, date, lines: [...] }
|
||||
```
|
||||
|
||||
## `order.delete`
|
||||
|
||||
*Source: `src/store/order/delete.js`*
|
||||
|
||||
Deletes an order by its id within the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the order to delete. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the order is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
await store.order.delete(42, 5);
|
||||
```
|
||||
|
||||
## `order.getById`
|
||||
|
||||
*Source: `src/store/order/getById.js`*
|
||||
|
||||
Returns a single order by its id within the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the order. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, number: string, type: string, entityId: number|null, date: string, lines: Array<object>}>` — The order with its lines.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the order is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const order = await store.order.getById(42, 5);
|
||||
// => { id, number, type, entityId, date, lines: [...] }
|
||||
```
|
||||
|
||||
## `order.list`
|
||||
|
||||
*Source: `src/store/order/list.js`*
|
||||
|
||||
Lists the orders of the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, number: string, type: string, entityId: number|null, date: string}>>` — The orders.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const orders = await store.order.list(42);
|
||||
// => [{ id, number, type, entityId, date }]
|
||||
```
|
||||
|
||||
## `order.update`
|
||||
|
||||
*Source: `src/store/order/update.js`*
|
||||
|
||||
Updates an order's header and/or lines.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the order to update. |
|
||||
| `patch` | `object` | The update patch. |
|
||||
| `[patch.number]` | `string` | The new order number. |
|
||||
| `[patch.date]` | `string` | The new order date. |
|
||||
| `[patch.entityId]` | `number` | The new customer entity id. |
|
||||
| `[patch.lines]` | `Array<object>` | The new order lines. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, number: string, type: string, entityId: number|null, date: string, lines: Array<object>}>` — The updated order.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the order is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const order = await store.order.update(42, 5, { number: 'B-2026-002', lines: [...] });
|
||||
// => { id, number, type, entityId, date, lines: [...] }
|
||||
```
|
||||
40
API.pic.md
40
API.pic.md
@@ -8,9 +8,25 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
Returns the full-size image (AVIF) for a given hash.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `fullHash` | `string` | The full image hash. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{buffer: Buffer, w: number, h: number, size: number, contentType: string}>` — The full image.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the image is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = getImageByHash(pool);
|
||||
const image = await fn(42, 'abc123');
|
||||
const image = await store.pic.getImageByHash(42, 'abc123');
|
||||
// => { buffer, w, h, size, contentType: 'image/avif' }
|
||||
```
|
||||
|
||||
@@ -20,8 +36,24 @@ const image = await fn(42, 'abc123');
|
||||
|
||||
Returns the thumbnail image (AVIF) for a given hash.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `fullHash` | `string` | The full image hash. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{buffer: Buffer, w: number, h: number, contentType: string}>` — The thumbnail image.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the image is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = getThumbByHash(pool);
|
||||
const thumb = await fn(42, 'abc123');
|
||||
const thumb = await store.pic.getThumbByHash(42, 'abc123');
|
||||
// => { buffer, w, h, contentType: 'image/avif' }
|
||||
```
|
||||
|
||||
@@ -6,22 +6,49 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
*Source: `src/store/posterminal/delete.js`*
|
||||
|
||||
Deletes a posterminal by its id within a company.
|
||||
Deletes a posterminal by its id within the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the posterminal to delete. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the posterminal is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = del(pool);
|
||||
await fn(42, 5);
|
||||
await store.posterminal.delete(42, 5);
|
||||
```
|
||||
|
||||
## `posterminal.list`
|
||||
|
||||
*Source: `src/store/posterminal/list.js`*
|
||||
|
||||
Lists the posterminals of a company.
|
||||
Lists the posterminals of the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, name: string, token: string, pairingpin: string}>>` — The posterminals.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = list(pool);
|
||||
const terminals = await fn(42);
|
||||
const terminals = await store.posterminal.list(42);
|
||||
// => [{ id, name, token, pairingpin }]
|
||||
```
|
||||
|
||||
@@ -31,9 +58,26 @@ const terminals = await fn(42);
|
||||
|
||||
Removes a category assignment from a posterminal (turns it off).
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `posterminalId` | `number` | The id of the posterminal. |
|
||||
| `categoryId` | `number` | The id of the category to remove. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, categoryIds: Array<number>}>` — The posterminal with its remaining category ids.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the posterminal is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = setOff(pool);
|
||||
const result = await fn(42, 5, 9);
|
||||
const result = await store.posterminal.setOff(42, 5, 9);
|
||||
// => { id, categoryIds: [...] }
|
||||
```
|
||||
|
||||
@@ -43,9 +87,25 @@ const result = await fn(42, 5, 9);
|
||||
|
||||
Removes all category assignments from a posterminal.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `posterminalId` | `number` | The id of the posterminal. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, categoryIds: Array<number>}>` — The posterminal with an empty category list.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the posterminal is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = setOffAll(pool);
|
||||
const result = await fn(42, 5);
|
||||
const result = await store.posterminal.setOffAll(42, 5);
|
||||
// => { id, categoryIds: [] }
|
||||
```
|
||||
|
||||
@@ -55,9 +115,26 @@ const result = await fn(42, 5);
|
||||
|
||||
Assigns a category to a posterminal (turns it on for that category).
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `posterminalId` | `number` | The id of the posterminal. |
|
||||
| `categoryId` | `number` | The id of the category to assign. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, categoryIds: Array<number>}>` — The posterminal with its category ids.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the posterminal is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = setOn(pool);
|
||||
const result = await fn(42, 5, 9);
|
||||
const result = await store.posterminal.setOn(42, 5, 9);
|
||||
// => { id, categoryIds: [9, ...] }
|
||||
```
|
||||
|
||||
@@ -65,11 +142,27 @@ const result = await fn(42, 5, 9);
|
||||
|
||||
*Source: `src/store/posterminal/setOnAll.js`*
|
||||
|
||||
Assigns all categories of a company to a posterminal.
|
||||
Assigns all categories of the user's selected company to a posterminal.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `posterminalId` | `number` | The id of the posterminal. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, categoryIds: Array<number>}>` — The posterminal with all category ids.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the posterminal is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = setOnAll(pool);
|
||||
const result = await fn(42, 5);
|
||||
const result = await store.posterminal.setOnAll(42, 5);
|
||||
// => { id, categoryIds: [...] }
|
||||
```
|
||||
|
||||
@@ -77,10 +170,30 @@ const result = await fn(42, 5);
|
||||
|
||||
*Source: `src/store/posterminal/upsert.js`*
|
||||
|
||||
Creates or updates a posterminal for a company.
|
||||
Creates or updates a posterminal for the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `input` | `object` | The posterminal input. |
|
||||
| `input.name` | `string` | The posterminal name. |
|
||||
| `input.pairingpin` | `string` | The pairing pin. |
|
||||
| `[input.token]` | `string` | Optional token. |
|
||||
| `[input.id]` | `number` | Optional id to update an existing posterminal. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, name: string, token: string, pairingpin: string}>` — The upserted posterminal.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the posterminal is invalid.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = upsert(pool);
|
||||
const terminal = await fn(42, { name: 'Kasse 1', pairingpin: '1234', token: 'abc' });
|
||||
const terminal = await store.posterminal.upsert(42, { name: 'Kasse 1', pairingpin: '1234', token: 'abc' });
|
||||
// => { id, name, token, pairingpin }
|
||||
```
|
||||
|
||||
146
API.quote.md
Normal file
146
API.quote.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# quote
|
||||
|
||||
This document is auto-generated from the JSDoc comments in `src/store` by running `npm run api-doc`.
|
||||
|
||||
## `quote.create`
|
||||
|
||||
*Source: `src/store/quote/create.js`*
|
||||
|
||||
Creates a new quote for the user's selected company with its lines.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `body` | `object` | The quote input. |
|
||||
| `body.number` | `string` | The quote number. |
|
||||
| `body.date` | `string` | The quote date. |
|
||||
| `[body.entityId]` | `number` | The id of the customer entity. |
|
||||
| `[body.lines]` | `Array<object>` | The quote lines. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, number: string, type: string, entityId: number|null, date: string, lines: Array<object>}>` — The created quote.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the quote is invalid.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const quote = await store.quote.create(42, { number: 'A-2026-001', date: '2026-09-01', entityId: 12, lines: [...] });
|
||||
// => { id, number, type, entityId, date, lines: [...] }
|
||||
```
|
||||
|
||||
## `quote.delete`
|
||||
|
||||
*Source: `src/store/quote/delete.js`*
|
||||
|
||||
Deletes a quote by its id within the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the quote to delete. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the quote is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
await store.quote.delete(42, 5);
|
||||
```
|
||||
|
||||
## `quote.getById`
|
||||
|
||||
*Source: `src/store/quote/getById.js`*
|
||||
|
||||
Returns a single quote by its id within the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the quote. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, number: string, type: string, entityId: number|null, date: string, lines: Array<object>}>` — The quote with its lines.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the quote is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const quote = await store.quote.getById(42, 5);
|
||||
// => { id, number, type, entityId, date, lines: [...] }
|
||||
```
|
||||
|
||||
## `quote.list`
|
||||
|
||||
*Source: `src/store/quote/list.js`*
|
||||
|
||||
Lists the quotes of the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, number: string, type: string, entityId: number|null, date: string}>>` — The quotes.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const quotes = await store.quote.list(42);
|
||||
// => [{ id, number, type, entityId, date }]
|
||||
```
|
||||
|
||||
## `quote.update`
|
||||
|
||||
*Source: `src/store/quote/update.js`*
|
||||
|
||||
Updates a quote's header and/or lines.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the quote to update. |
|
||||
| `patch` | `object` | The update patch. |
|
||||
| `[patch.number]` | `string` | The new quote number. |
|
||||
| `[patch.date]` | `string` | The new quote date. |
|
||||
| `[patch.entityId]` | `number` | The new customer entity id. |
|
||||
| `[patch.lines]` | `Array<object>` | The new quote lines. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, number: string, type: string, entityId: number|null, date: string, lines: Array<object>}>` — The updated quote.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the quote is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const quote = await store.quote.update(42, 5, { number: 'A-2026-002', lines: [...] });
|
||||
// => { id, number, type, entityId, date, lines: [...] }
|
||||
```
|
||||
@@ -6,11 +6,28 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
*Source: `src/store/supplier/create.js`*
|
||||
|
||||
Creates a new supplier for a company, recording history.
|
||||
Creates a new supplier for the user's selected company, recording history.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `input` | `object` | The supplier input. |
|
||||
| `input.name` | `string` | The supplier name. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, name: string, createdAt: string, updatedAt: string}>` — The created supplier.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the name is empty.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = create(pool);
|
||||
const supplier = await fn(42, { name: 'Acme Supplies' });
|
||||
const supplier = await store.supplier.create(42, { name: 'Acme Supplies' });
|
||||
// => { id, name, createdAt, updatedAt }
|
||||
```
|
||||
|
||||
@@ -18,22 +35,49 @@ const supplier = await fn(42, { name: 'Acme Supplies' });
|
||||
|
||||
*Source: `src/store/supplier/delete.js`*
|
||||
|
||||
Deletes a supplier by its id within a company.
|
||||
Deletes a supplier by its id within the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the supplier to delete. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the supplier is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = del(pool);
|
||||
await fn(42, 5);
|
||||
await store.supplier.delete(42, 5);
|
||||
```
|
||||
|
||||
## `supplier.list`
|
||||
|
||||
*Source: `src/store/supplier/list.js`*
|
||||
|
||||
Lists the suppliers of a company.
|
||||
Lists the suppliers of the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, name: string, createdAt: string, updatedAt: string}>>` — The suppliers.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = list(pool);
|
||||
const suppliers = await fn(42);
|
||||
const suppliers = await store.supplier.list(42);
|
||||
// => [{ id, name, createdAt, updatedAt }]
|
||||
```
|
||||
|
||||
@@ -43,8 +87,26 @@ const suppliers = await fn(42);
|
||||
|
||||
Updates the name of a supplier, recording history.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `id` | `number` | The id of the supplier to update. |
|
||||
| `input` | `object` | The update input. |
|
||||
| `input.name` | `string` | The new supplier name. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, name: string, createdAt: string, updatedAt: string}>` — The updated supplier.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the supplier is not found or the name is empty.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = update(pool);
|
||||
const supplier = await fn(42, 5, { name: 'Acme GmbH' });
|
||||
const supplier = await store.supplier.update(42, 5, { name: 'Acme GmbH' });
|
||||
// => { id, name, createdAt, updatedAt }
|
||||
```
|
||||
|
||||
45
API.tax.md
45
API.tax.md
@@ -8,6 +8,19 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
Creates a new tax rate.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `rate` | `number` | The tax rate in percent (e.g. `19`). |
|
||||
| `[tx]` | `object` | Optional transaction to run within. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<number>` — The id of the created tax rate.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const id = await store.tax.create(19);
|
||||
// => 1
|
||||
@@ -19,6 +32,19 @@ const id = await store.tax.create(19);
|
||||
|
||||
Returns the id of a tax rate, creating it if it does not exist.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `rate` | `number` | The tax rate in percent (e.g. `19`). |
|
||||
| `[tx]` | `object` | Optional transaction to run within. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<number>` — The id of the tax rate.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const id = await store.tax.ensure(19);
|
||||
// => 1
|
||||
@@ -30,6 +56,19 @@ const id = await store.tax.ensure(19);
|
||||
|
||||
Returns a tax row by its rate.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `rate` | `number` | The tax rate in percent (e.g. `19`). |
|
||||
| `[tx]` | `object` | Optional transaction to run within. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, rate: number}>` — The tax row, or `undefined` if not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const tax = await store.tax.getByRate(19);
|
||||
// => { id: 1, rate: 19 }
|
||||
@@ -41,6 +80,12 @@ const tax = await store.tax.getByRate(19);
|
||||
|
||||
Lists all tax rates.
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, rate: number}>>` — All tax rate rows.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const taxes = await store.tax.list();
|
||||
// => [{ id: 1, rate: 19 }]
|
||||
|
||||
66
API.unit.md
66
API.unit.md
@@ -6,22 +6,49 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
*Source: `src/store/unit/delete.js`*
|
||||
|
||||
Deletes a unit for a company by its SI code.
|
||||
Deletes a unit for the user's selected company by its SI code.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `si` | `string` | The SI code of the unit to delete. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the unit is not found.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = del(pool);
|
||||
await fn(42, 'l');
|
||||
await store.unit.delete(42, 'l');
|
||||
```
|
||||
|
||||
## `unit.list`
|
||||
|
||||
*Source: `src/store/unit/list.js`*
|
||||
|
||||
Lists the units available for a company.
|
||||
Lists the units available for the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, si: string, basesi: string, basesifactor: number, symbol: string, name: string}>>` — The units.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = list(pool);
|
||||
const units = await fn(42);
|
||||
const units = await store.unit.list(42);
|
||||
// => [{ id, si, basesi, basesifactor, symbol, name }]
|
||||
```
|
||||
|
||||
@@ -29,10 +56,31 @@ const units = await fn(42);
|
||||
|
||||
*Source: `src/store/unit/upsert.js`*
|
||||
|
||||
Creates or updates a unit for a company, keyed by its SI code.
|
||||
Creates or updates a unit for the user's selected company, keyed by its SI code.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `input` | `object` | The unit input. |
|
||||
| `input.si` | `string` | The SI code. |
|
||||
| `input.basesi` | `string` | The base SI code. |
|
||||
| `input.basesifactor` | `number` | The base SI factor (must be > 0). |
|
||||
| `input.symbol` | `string` | The unit symbol. |
|
||||
| `input.name` | `string` | The unit name. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, si: string, basesi: string, basesifactor: number, symbol: string, name: string}>` — The upserted unit.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — If the unit is invalid.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const fn = upsert(pool);
|
||||
const unit = await fn(42, { si: 'l', basesi: 'm3', basesifactor: 0.001, symbol: 'L', name: 'Liter' });
|
||||
const unit = await store.unit.upsert(42, { si: 'l', basesi: 'm3', basesifactor: 0.001, symbol: 'L', name: 'Liter' });
|
||||
// => { id, si, basesi, basesifactor, symbol, name }
|
||||
```
|
||||
|
||||
@@ -58,13 +58,27 @@ const INTERNAL_FILES = new Set([
|
||||
'peekNumber.js',
|
||||
'shape.js',
|
||||
'createStore.js',
|
||||
'manifestLines.js',
|
||||
'index.js',
|
||||
]);
|
||||
|
||||
/**
|
||||
* Strip the outer braces from a JSDoc type (e.g. `{number}` -> `number`),
|
||||
* preserving nested braces inside generics.
|
||||
* @param {string} type
|
||||
* @returns {string}
|
||||
*/
|
||||
function stripBraces(type) {
|
||||
if (type.startsWith('{') && type.endsWith('}')) {
|
||||
return type.slice(1, -1);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the leading JSDoc block from a source string.
|
||||
* @param {string} source
|
||||
* @returns {{ description: string, example: string } | null}
|
||||
* @returns {{ description: string, example: string, params: Array<object>, returns: string|null, throws: string|null } | null}
|
||||
*/
|
||||
function extractJsDoc(source) {
|
||||
const match = source.match(/\/\*\*([\s\S]*?)\*\//);
|
||||
@@ -77,27 +91,53 @@ function extractJsDoc(source) {
|
||||
|
||||
const description = [];
|
||||
const example = [];
|
||||
const params = [];
|
||||
let returns = null;
|
||||
let throws = null;
|
||||
let inExample = false;
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.trim() === '@example') {
|
||||
const trimmed = line.trim();
|
||||
if (trimmed === '@example') {
|
||||
inExample = true;
|
||||
continue;
|
||||
}
|
||||
if (line.trim().startsWith('@')) {
|
||||
if (trimmed.startsWith('@')) {
|
||||
inExample = false;
|
||||
const paramMatch = trimmed.match(/^@param\s+(\{.*\})\s+(\S+)\s*[-–]?\s*(.*)$/);
|
||||
if (paramMatch) {
|
||||
params.push({
|
||||
type: stripBraces(paramMatch[1]),
|
||||
name: paramMatch[2],
|
||||
description: paramMatch[3].trim(),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const returnsMatch = trimmed.match(/^@returns\s+(\{.*\})\s*[-–]?\s*(.*)$/);
|
||||
if (returnsMatch) {
|
||||
returns = { type: stripBraces(returnsMatch[1]), description: returnsMatch[2].trim() };
|
||||
continue;
|
||||
}
|
||||
const throwsMatch = trimmed.match(/^@throws\s+(\{.*\})\s*[-–]?\s*(.*)$/);
|
||||
if (throwsMatch) {
|
||||
throws = { type: stripBraces(throwsMatch[1]), description: throwsMatch[2].trim() };
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (inExample) {
|
||||
example.push(line);
|
||||
} else if (line.trim()) {
|
||||
description.push(line.trim());
|
||||
} else if (trimmed) {
|
||||
description.push(trimmed);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
description: description.join(' ').trim(),
|
||||
example: example.join('\n').trim(),
|
||||
params,
|
||||
returns,
|
||||
throws,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -146,6 +186,9 @@ function buildModules() {
|
||||
file: rel.join('/'),
|
||||
description: doc.description,
|
||||
example: doc.example,
|
||||
params: doc.params,
|
||||
returns: doc.returns,
|
||||
throws: doc.throws,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -170,7 +213,31 @@ function renderSection(moduleName, entries) {
|
||||
lines.push(entry.description);
|
||||
lines.push('');
|
||||
}
|
||||
if (entry.params && entry.params.length) {
|
||||
lines.push('**Parameters**');
|
||||
lines.push('');
|
||||
lines.push('| Name | Type | Description |');
|
||||
lines.push('| --- | --- | --- |');
|
||||
for (const p of entry.params) {
|
||||
lines.push(`| \`${p.name}\` | \`${p.type}\` | ${p.description} |`);
|
||||
}
|
||||
lines.push('');
|
||||
}
|
||||
if (entry.returns) {
|
||||
lines.push('**Returns**');
|
||||
lines.push('');
|
||||
lines.push(`\`${entry.returns.type}\`${entry.returns.description ? ` — ${entry.returns.description}` : ''}`);
|
||||
lines.push('');
|
||||
}
|
||||
if (entry.throws) {
|
||||
lines.push('**Throws**');
|
||||
lines.push('');
|
||||
lines.push(`\`${entry.throws.type}\`${entry.throws.description ? ` — ${entry.throws.description}` : ''}`);
|
||||
lines.push('');
|
||||
}
|
||||
if (entry.example) {
|
||||
lines.push('**Example**');
|
||||
lines.push('');
|
||||
lines.push('```js');
|
||||
lines.push(entry.example);
|
||||
lines.push('```');
|
||||
|
||||
29
src/index.js
29
src/index.js
@@ -68,8 +68,16 @@ import deliveryGetById from './store/delivery/getById.js';
|
||||
import deliveryCreate from './store/delivery/create.js';
|
||||
import deliveryUpdate from './store/delivery/update.js';
|
||||
import deliveryDelete from './store/delivery/delete.js';
|
||||
import createQuoteStore from './store/quote/index.js';
|
||||
import createOrderStore from './store/order/index.js';
|
||||
import quoteList from './store/quote/list.js';
|
||||
import quoteGetById from './store/quote/getById.js';
|
||||
import quoteCreate from './store/quote/create.js';
|
||||
import quoteUpdate from './store/quote/update.js';
|
||||
import quoteDelete from './store/quote/delete.js';
|
||||
import orderList from './store/order/list.js';
|
||||
import orderGetById from './store/order/getById.js';
|
||||
import orderCreate from './store/order/create.js';
|
||||
import orderUpdate from './store/order/update.js';
|
||||
import orderDelete from './store/order/delete.js';
|
||||
import posterminalList from './store/posterminal/list.js';
|
||||
import posterminalUpsert from './store/posterminal/upsert.js';
|
||||
import posterminalDelete from './store/posterminal/delete.js';
|
||||
@@ -227,8 +235,21 @@ export default class LibDashStore {
|
||||
delete: deliveryDelete(pool),
|
||||
};
|
||||
|
||||
this.quote = createQuoteStore(pool);
|
||||
this.order = createOrderStore(pool);
|
||||
this.quote = {
|
||||
list: quoteList(pool),
|
||||
getById: quoteGetById(pool),
|
||||
create: quoteCreate(pool),
|
||||
update: quoteUpdate(pool),
|
||||
delete: quoteDelete(pool),
|
||||
};
|
||||
|
||||
this.order = {
|
||||
list: orderList(pool),
|
||||
getById: orderGetById(pool),
|
||||
create: orderCreate(pool),
|
||||
update: orderUpdate(pool),
|
||||
delete: orderDelete(pool),
|
||||
};
|
||||
|
||||
this.posterminal = {
|
||||
list: posterminalList(pool),
|
||||
|
||||
@@ -5,20 +5,21 @@ import newFullHash from '../../util/newFullHash.js';
|
||||
import request from '../request.js';
|
||||
import resolveArticleBySku from './resolveArticleBySku.js';
|
||||
|
||||
/**
|
||||
* Adds an image to an article by its SKU, processing it into AVIF variants.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that adds an article image.
|
||||
*
|
||||
* @example
|
||||
* const fn = addImage(pool);
|
||||
* const result = await fn(42, 'SKU-1', imageBuffer);
|
||||
* // => { picId, sort, full: { w, h, size }, thumb: { w, h }, fullHash, thumbHash }
|
||||
*/
|
||||
export default function addImage(pool) {
|
||||
const resolve = resolveArticleBySku(pool);
|
||||
|
||||
/**
|
||||
* Adds an image to an article by its SKU, processing it into AVIF variants.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {string} sku - The article SKU.
|
||||
* @param {Buffer} image - The source image buffer.
|
||||
* @returns {Promise<{picId: number, sort: number, full: {w: number, h: number, size: number}, thumb: {w: number, h: number}, fullHash: string, thumbHash: string}>} The added image info.
|
||||
*
|
||||
* @example
|
||||
* const result = await store.article.addImage(42, 'SKU-1', imageBuffer);
|
||||
* // => { picId, sort, full: { w, h, size }, thumb: { w, h }, fullHash, thumbHash }
|
||||
*/
|
||||
return async (googleAuthId, sku, image) => {
|
||||
const { articleId } = await resolve(googleAuthId, sku);
|
||||
|
||||
|
||||
@@ -7,23 +7,25 @@ import nextSku from '../numbers/nextSku.js';
|
||||
import request from '../request.js';
|
||||
import slugify from '../slugify.js';
|
||||
|
||||
/**
|
||||
* Creates a new article under a given parent category, allocating a SKU and
|
||||
* a default unit.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that creates an article.
|
||||
*
|
||||
* @example
|
||||
* const fn = createUnderParent(pool);
|
||||
* const article = await fn({ googleAuthId: 42, parentId: 5, name: 'Cola' });
|
||||
* // => { id, parentId, name, sku }
|
||||
*/
|
||||
export default function createUnderParent(pool) {
|
||||
const access = resolveCategoryAccess(pool);
|
||||
const ensureUnit = ensureDefaultUnit(pool);
|
||||
const allocateSku = nextSku(pool);
|
||||
|
||||
/**
|
||||
* Creates a new article under a given parent category, allocating a SKU and
|
||||
* a default unit.
|
||||
*
|
||||
* @param {object} input - The article creation input.
|
||||
* @param {number} input.googleAuthId - The id of the Google auth user.
|
||||
* @param {number} input.parentId - The id of the parent category.
|
||||
* @param {string} input.name - The article name.
|
||||
* @returns {Promise<{id: number, parentId: number, name: string, sku: string}>} The created article.
|
||||
*
|
||||
* @example
|
||||
* const article = await store.article.createUnderParent({ googleAuthId: 42, parentId: 5, name: 'Cola' });
|
||||
* // => { id, parentId, name, sku }
|
||||
*/
|
||||
return async ({ googleAuthId, parentId, name }) => {
|
||||
const { companyId, languageId } = await access(googleAuthId, parentId);
|
||||
|
||||
|
||||
@@ -11,19 +11,20 @@ import {
|
||||
import resolveArticleBySku from './resolveArticleBySku.js';
|
||||
import request from '../request.js';
|
||||
|
||||
/**
|
||||
* Deletes an article by its SKU, refusing when it still has children.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that deletes an article.
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 'SKU-1');
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const resolve = resolveArticleBySku(pool);
|
||||
|
||||
/**
|
||||
* Deletes an article by its SKU, refusing when it still has children.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {string} sku - The article SKU.
|
||||
* @returns {Promise<void>}
|
||||
* @throws {Error} If the article has children or is not found.
|
||||
*
|
||||
* @example
|
||||
* await store.article.delete(42, 'SKU-1');
|
||||
*/
|
||||
return async (googleAuthId, sku) => {
|
||||
const { articleId, languageId } = await resolve(googleAuthId, sku);
|
||||
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import { DELETE_BY_ARTICLE_AND_HASH } from '../../sql/pic.js';
|
||||
import resolveArticleBySku from './resolveArticleBySku.js';
|
||||
|
||||
/**
|
||||
* Deletes an image from an article by its SKU and full image hash.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that deletes an article image.
|
||||
*
|
||||
* @example
|
||||
* const fn = deleteImage(pool);
|
||||
* await fn(42, 'SKU-1', 'abc123');
|
||||
*/
|
||||
export default function deleteImage(pool) {
|
||||
const resolve = resolveArticleBySku(pool);
|
||||
|
||||
/**
|
||||
* Deletes an image from an article by its SKU and full image hash.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {string} sku - The article SKU.
|
||||
* @param {string} fullHash - The full image hash to delete.
|
||||
* @returns {Promise<void>}
|
||||
* @throws {Error} If the image is not found.
|
||||
*
|
||||
* @example
|
||||
* await store.article.deleteImage(42, 'SKU-1', 'abc123');
|
||||
*/
|
||||
return async (googleAuthId, sku, fullHash) => {
|
||||
const { articleId } = await resolve(googleAuthId, sku);
|
||||
|
||||
|
||||
@@ -6,19 +6,20 @@ function parseJsonArray(raw) {
|
||||
return Array.isArray(arr) ? arr : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full detail of an article by its SKU, including unit, brand,
|
||||
* categories, photos, EANs and prices.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that fetches an article by SKU.
|
||||
*
|
||||
* @example
|
||||
* const fn = getBySku(pool);
|
||||
* const article = await fn(42, 'SKU-1');
|
||||
* // => { article: { id, sku, name, unit, brand, categoryIds, photos, eans, prices, ... } }
|
||||
*/
|
||||
export default function getBySku(pool) {
|
||||
/**
|
||||
* Returns the full detail of an article by its SKU, including unit, brand,
|
||||
* categories, photos, EANs and prices.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {string} sku - The article SKU.
|
||||
* @returns {Promise<{article: object}>} The full article detail.
|
||||
* @throws {Error} If the article is not found or the user is not authorized.
|
||||
*
|
||||
* @example
|
||||
* const article = await store.article.getBySku(42, 'SKU-1');
|
||||
* // => { article: { id, sku, name, unit, brand, categoryIds, photos, eans, prices, ... } }
|
||||
*/
|
||||
return async (googleAuthId, sku) => {
|
||||
const t0 = performance.now();
|
||||
const result = await pool
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { LIST_BY_ARTICLE_ID } from '../../sql/articlehistory.js';
|
||||
import resolveArticleBySku from './resolveArticleBySku.js';
|
||||
|
||||
/**
|
||||
* Returns the change history of an article by its SKU.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that fetches the article history.
|
||||
*
|
||||
* @example
|
||||
* const fn = getHistoryBySku(pool);
|
||||
* const history = await fn(42, 'SKU-1');
|
||||
* // => [{ id, companyuserId, date, text }]
|
||||
*/
|
||||
export default function getHistoryBySku(pool) {
|
||||
const resolve = resolveArticleBySku(pool);
|
||||
|
||||
/**
|
||||
* Returns the change history of an article by its SKU.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {string} sku - The article SKU.
|
||||
* @returns {Promise<Array<{id: number, companyuserId: number|null, date: string, text: string}>>} The article history entries.
|
||||
*
|
||||
* @example
|
||||
* const history = await store.article.getHistoryBySku(42, 'SKU-1');
|
||||
* // => [{ id, companyuserId, date, text }]
|
||||
*/
|
||||
return async (googleAuthId, sku) => {
|
||||
const { articleId } = await resolve(googleAuthId, sku);
|
||||
|
||||
|
||||
@@ -10,22 +10,23 @@ function parseJsonArray(raw) {
|
||||
return Array.isArray(arr) ? arr : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns articles for export, optionally scoped to a category, including
|
||||
* category breadcrumbs and image hashes.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that lists articles for export.
|
||||
*
|
||||
* @example
|
||||
* const fn = listExport(pool);
|
||||
* const articles = await fn(42, null);
|
||||
* // => [{ id, sku, name, slug, unitSi, net, taxRate, eans, categoryBreadcrumbs, fullHashes, ... }]
|
||||
*/
|
||||
export default function listExport(pool) {
|
||||
const companyAccess = resolveSelectedCompanyAccess(pool);
|
||||
const categoryAccess = resolveCategoryAccess(pool);
|
||||
|
||||
/**
|
||||
* Returns articles for export, optionally scoped to a category, including
|
||||
* category breadcrumbs and image hashes.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number|null} [categoryId] - Optional category id to scope the export to; defaults to the company root.
|
||||
* @returns {Promise<Array<object>>} The articles for export.
|
||||
* @throws {Error} If the category is not reachable from the selected company.
|
||||
*
|
||||
* @example
|
||||
* const articles = await store.article.listExport(42, null);
|
||||
* // => [{ id, sku, name, slug, unitSi, net, taxRate, eans, categoryBreadcrumbs, fullHashes, ... }]
|
||||
*/
|
||||
return async (googleAuthId, categoryId) => {
|
||||
const { languageId, posCustomerGroupId, rootCategoryId } =
|
||||
await companyAccess(googleAuthId);
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import { SEARCH_ARTICLES } from '../../sql/article.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Returns a lightweight search index of all articles for a company.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that lists the article search index.
|
||||
*
|
||||
* @example
|
||||
* const fn = listSearchIndex(pool);
|
||||
* const articles = await fn(42);
|
||||
* // => [{ id, sku, name, eans, categoryIds }]
|
||||
*/
|
||||
export default function listSearchIndex(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Returns a lightweight search index of all articles for the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @returns {Promise<Array<{id: number, sku: string, name: string, eans: Array<string>, categoryIds: Array<number>}>>} The article search index.
|
||||
*
|
||||
* @example
|
||||
* const articles = await store.article.listSearchIndex(42);
|
||||
* // => [{ id, sku, name, eans, categoryIds }]
|
||||
*/
|
||||
return async (googleAuthId) => {
|
||||
const { companyId, rootCategoryId, languageId } = await access(googleAuthId);
|
||||
|
||||
|
||||
@@ -6,20 +6,23 @@ import {
|
||||
import resolveArticleBySku from './resolveArticleBySku.js';
|
||||
import request from '../request.js';
|
||||
|
||||
/**
|
||||
* Reorders the images of an article by its SKU.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that reorders article images.
|
||||
*
|
||||
* @example
|
||||
* const fn = orderImages(pool);
|
||||
* const result = await fn(42, 'SKU-1', { fullHashes: ['a', 'b'] });
|
||||
* // => { fullHashes: ['a', 'b'] }
|
||||
*/
|
||||
export default function orderImages(pool) {
|
||||
const resolve = resolveArticleBySku(pool);
|
||||
|
||||
/**
|
||||
* Reorders the images of an article by its SKU.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {string} sku - The article SKU.
|
||||
* @param {object} input - The reorder input.
|
||||
* @param {Array<string>} input.fullHashes - The full image hashes in their new order.
|
||||
* @returns {Promise<{fullHashes: Array<string>}>} The applied order.
|
||||
* @throws {Error} If the order is invalid.
|
||||
*
|
||||
* @example
|
||||
* const result = await store.article.orderImages(42, 'SKU-1', { fullHashes: ['a', 'b'] });
|
||||
* // => { fullHashes: ['a', 'b'] }
|
||||
*/
|
||||
return async (googleAuthId, sku, { fullHashes }) => {
|
||||
if (!Array.isArray(fullHashes)) {
|
||||
throw new Error('Invalid image order');
|
||||
|
||||
@@ -5,21 +5,23 @@ import {
|
||||
import queryBySku from './queryBySku.js';
|
||||
import resolveArticleBySku from './resolveArticleBySku.js';
|
||||
|
||||
/**
|
||||
* Sets (or clears) the brand of an article by its SKU.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that sets the article brand.
|
||||
*
|
||||
* @example
|
||||
* const fn = setBrandIdBySku(pool);
|
||||
* const article = await fn(42, 'SKU-1', 3);
|
||||
* // => { id, sku, name, ... }
|
||||
*/
|
||||
export default function setBrandIdBySku(pool) {
|
||||
const resolve = resolveArticleBySku(pool);
|
||||
const load = queryBySku(pool);
|
||||
|
||||
/**
|
||||
* Sets (or clears) the brand of an article by its SKU.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {string} sku - The article SKU.
|
||||
* @param {number|null} brandId - The brand id, or `null` to clear the brand.
|
||||
* @returns {Promise<object>} The updated article.
|
||||
* @throws {Error} If the brand or article is not found.
|
||||
*
|
||||
* @example
|
||||
* const article = await store.article.setBrandIdBySku(42, 'SKU-1', 3);
|
||||
* // => { id, sku, name, ... }
|
||||
*/
|
||||
return async (googleAuthId, sku, brandId) => {
|
||||
const { articleId, languageId, companyId } = await resolve(
|
||||
googleAuthId,
|
||||
|
||||
@@ -7,22 +7,24 @@ import resolveCategoryAccess from '../category/resolveCategoryAccess.js';
|
||||
import queryBySku from './queryBySku.js';
|
||||
import resolveArticleBySku from './resolveArticleBySku.js';
|
||||
|
||||
/**
|
||||
* Replaces the category membership of an article by its SKU.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that sets the article categories.
|
||||
*
|
||||
* @example
|
||||
* const fn = setCategoryIdsBySku(pool);
|
||||
* const article = await fn(42, 'SKU-1', [5, 9]);
|
||||
* // => { id, sku, name, ... }
|
||||
*/
|
||||
export default function setCategoryIdsBySku(pool) {
|
||||
const resolve = resolveArticleBySku(pool);
|
||||
const categoryAccess = resolveCategoryAccess(pool);
|
||||
const load = queryBySku(pool);
|
||||
|
||||
/**
|
||||
* Replaces the category membership of an article by its SKU.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {string} sku - The article SKU.
|
||||
* @param {Array<number>} categoryIds - The category ids the article should belong to (at least one).
|
||||
* @returns {Promise<object>} The updated article.
|
||||
* @throws {Error} If the category ids are invalid or the article is not found.
|
||||
*
|
||||
* @example
|
||||
* const article = await store.article.setCategoryIdsBySku(42, 'SKU-1', [5, 9]);
|
||||
* // => { id, sku, name, ... }
|
||||
*/
|
||||
return async (googleAuthId, sku, categoryIds) => {
|
||||
if (!Array.isArray(categoryIds)) {
|
||||
throw new Error('Invalid categoryIds');
|
||||
|
||||
@@ -6,21 +6,23 @@ import request from '../request.js';
|
||||
import queryBySku from './queryBySku.js';
|
||||
import resolveArticleBySku from './resolveArticleBySku.js';
|
||||
|
||||
/**
|
||||
* Replaces the EAN list of an article by its SKU.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that sets article EANs.
|
||||
*
|
||||
* @example
|
||||
* const fn = setEansBySku(pool);
|
||||
* const article = await fn(42, 'SKU-1', ['4001234567890']);
|
||||
* // => { id, sku, name, ... }
|
||||
*/
|
||||
export default function setEansBySku(pool) {
|
||||
const resolve = resolveArticleBySku(pool);
|
||||
const load = queryBySku(pool);
|
||||
|
||||
/**
|
||||
* Replaces the EAN list of an article by its SKU.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {string} sku - The article SKU.
|
||||
* @param {Array<string>} eans - The EANs to set (must be unique, non-empty strings).
|
||||
* @returns {Promise<object>} The updated article.
|
||||
* @throws {Error} If the EANs are invalid or the article is not found.
|
||||
*
|
||||
* @example
|
||||
* const article = await store.article.setEansBySku(42, 'SKU-1', ['4001234567890']);
|
||||
* // => { id, sku, name, ... }
|
||||
*/
|
||||
return async (googleAuthId, sku, eans) => {
|
||||
if (!Array.isArray(eans)) {
|
||||
throw new Error('Invalid eans');
|
||||
|
||||
@@ -42,22 +42,24 @@ function normalizePrices(prices) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the price list of an article by its SKU.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that sets article prices.
|
||||
*
|
||||
* @example
|
||||
* const fn = setPricesBySku(pool);
|
||||
* const article = await fn(42, 'SKU-1', [{ price: 1.5, tax: 19, minmultiplier: 1 }]);
|
||||
* // => { id, sku, name, ... }
|
||||
*/
|
||||
export default function setPricesBySku(pool) {
|
||||
const resolve = resolveArticleBySku(pool);
|
||||
const ensureTax = taxEnsure(pool);
|
||||
const load = queryBySku(pool);
|
||||
|
||||
/**
|
||||
* Replaces the price list of an article by its SKU.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {string} sku - The article SKU.
|
||||
* @param {Array<{price: number, tax: number, minmultiplier?: number}>} prices - The price entries (minmultiplier defaults to 1).
|
||||
* @returns {Promise<object>} The updated article.
|
||||
* @throws {Error} If the prices are invalid or the article is not found.
|
||||
*
|
||||
* @example
|
||||
* const article = await store.article.setPricesBySku(42, 'SKU-1', [{ price: 1.5, tax: 19, minmultiplier: 1 }]);
|
||||
* // => { id, sku, name, ... }
|
||||
*/
|
||||
return async (googleAuthId, sku, prices) => {
|
||||
const normalized = normalizePrices(prices);
|
||||
|
||||
|
||||
@@ -2,21 +2,23 @@ import { UPDATE_WEIGHT } from '../../sql/article.js';
|
||||
import queryBySku from './queryBySku.js';
|
||||
import resolveArticleBySku from './resolveArticleBySku.js';
|
||||
|
||||
/**
|
||||
* Sets the weight of an article by its SKU.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that sets the article weight.
|
||||
*
|
||||
* @example
|
||||
* const fn = setWeightBySku(pool);
|
||||
* const article = await fn(42, 'SKU-1', 1.5);
|
||||
* // => { id, sku, name, ... }
|
||||
*/
|
||||
export default function setWeightBySku(pool) {
|
||||
const resolve = resolveArticleBySku(pool);
|
||||
const load = queryBySku(pool);
|
||||
|
||||
/**
|
||||
* Sets the weight of an article by its SKU.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {string} sku - The article SKU.
|
||||
* @param {number} weight - The weight (must be >= 0).
|
||||
* @returns {Promise<object>} The updated article.
|
||||
* @throws {Error} If the weight is invalid or the article is not found.
|
||||
*
|
||||
* @example
|
||||
* const article = await store.article.setWeightBySku(42, 'SKU-1', 1.5);
|
||||
* // => { id, sku, name, ... }
|
||||
*/
|
||||
return async (googleAuthId, sku, weight) => {
|
||||
if (!Number.isFinite(weight) || weight < 0) {
|
||||
throw new Error('Invalid weight');
|
||||
|
||||
@@ -20,21 +20,26 @@ function formatUnitLabel(amount, symbol) {
|
||||
return `${Number(amount)}${sym}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an article's name and/or unit (SI and amount), recording history.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that updates an article by SKU.
|
||||
*
|
||||
* @example
|
||||
* const fn = updateBySku(pool);
|
||||
* const article = await fn(42, 'SKU-1', { name: 'Cola 1L', unitSi: 'l', unitamount: 1 });
|
||||
* // => { id, sku, name, ... }
|
||||
*/
|
||||
export default function updateBySku(pool) {
|
||||
const resolve = resolveArticleBySku(pool);
|
||||
const load = queryBySku(pool);
|
||||
|
||||
/**
|
||||
* Updates an article's name and/or unit (SI and amount), recording history.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {string} sku - The article SKU.
|
||||
* @param {object} patch - The update patch.
|
||||
* @param {string} [patch.name] - The new article name.
|
||||
* @param {string} [patch.unitSi] - The new unit SI code.
|
||||
* @param {number} [patch.unitamount] - The new unit amount (must be > 0).
|
||||
* @returns {Promise<object>} The updated article.
|
||||
* @throws {Error} If no valid field is provided or the article is not found.
|
||||
*
|
||||
* @example
|
||||
* const article = await store.article.updateBySku(42, 'SKU-1', { name: 'Cola 1L', unitSi: 'l', unitamount: 1 });
|
||||
* // => { id, sku, name, ... }
|
||||
*/
|
||||
return async (googleAuthId, sku, patch = {}) => {
|
||||
const hasName = Object.prototype.hasOwnProperty.call(patch, 'name');
|
||||
const hasUnitamount = Object.prototype.hasOwnProperty.call(patch, 'unitamount');
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { DELETE_BY_COMPANY_AND_ID } from '../../sql/brand.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes a brand for a company by its id.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that deletes a brand.
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 3);
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Deletes a brand for the user's selected company by its id.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} id - The id of the brand to delete.
|
||||
* @returns {Promise<void>}
|
||||
* @throws {Error} If the brand is not found.
|
||||
*
|
||||
* @example
|
||||
* await store.brand.delete(42, 3);
|
||||
*/
|
||||
return async (googleAuthId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Brand not found');
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import { LIST_BY_COMPANY } from '../../sql/brand.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Lists the brands of a company.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that lists brands.
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const brands = await fn(42);
|
||||
* // => [{ id, name, gsprpinfo }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Lists the brands of the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @returns {Promise<Array<{id: number, name: string, gsprpinfo: string|null}>>} The brands.
|
||||
*
|
||||
* @example
|
||||
* const brands = await store.brand.list(42);
|
||||
* // => [{ id, name, gsprpinfo }]
|
||||
*/
|
||||
return async (googleAuthId) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
|
||||
@@ -5,20 +5,23 @@ import {
|
||||
} from '../../sql/brand.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Creates or updates a brand for a company, keyed by name.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that upserts a brand.
|
||||
*
|
||||
* @example
|
||||
* const fn = upsert(pool);
|
||||
* const brand = await fn(42, { name: 'Coca-Cola', gsprpinfo: 'x' });
|
||||
* // => { id, name, gsprpinfo }
|
||||
*/
|
||||
export default function upsert(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Creates or updates a brand for the user's selected company, keyed by name.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {object} input - The brand input.
|
||||
* @param {string} input.name - The brand name.
|
||||
* @param {string} [input.gsprpinfo] - Optional GSPRP info.
|
||||
* @returns {Promise<{id: number, name: string, gsprpinfo: string|null}>} The upserted brand.
|
||||
* @throws {Error} If the brand is invalid.
|
||||
*
|
||||
* @example
|
||||
* const brand = await store.brand.upsert(42, { name: 'Coca-Cola', gsprpinfo: 'x' });
|
||||
* // => { id, name, gsprpinfo }
|
||||
*/
|
||||
return async (googleAuthId, input = {}) => {
|
||||
const name = input.name?.trim();
|
||||
if (!name) {
|
||||
|
||||
@@ -5,20 +5,21 @@ import newFullHash from '../../util/newFullHash.js';
|
||||
import request from '../request.js';
|
||||
import resolveCategoryAccess from './resolveCategoryAccess.js';
|
||||
|
||||
/**
|
||||
* Adds an image to a category, processing it into AVIF full and thumb variants.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that adds a category image.
|
||||
*
|
||||
* @example
|
||||
* const fn = addImage(pool);
|
||||
* const result = await fn(42, 5, imageBuffer);
|
||||
* // => { id, fullHash, thumbW, thumbH, thumbHash }
|
||||
*/
|
||||
export default function addImage(pool) {
|
||||
const access = resolveCategoryAccess(pool);
|
||||
|
||||
/**
|
||||
* Adds an image to a category, processing it into AVIF full and thumb variants.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} categoryId - The id of the category.
|
||||
* @param {Buffer} image - The source image buffer.
|
||||
* @returns {Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>} The added image info.
|
||||
*
|
||||
* @example
|
||||
* const result = await store.category.addImage(42, 5, imageBuffer);
|
||||
* // => { id, fullHash, thumbW, thumbH, thumbHash }
|
||||
*/
|
||||
return async (googleAuthId, categoryId, image) => {
|
||||
await access(googleAuthId, categoryId);
|
||||
|
||||
|
||||
@@ -7,20 +7,22 @@ import resolveCategoryAccess from './resolveCategoryAccess.js';
|
||||
import request from '../request.js';
|
||||
import slugify from '../slugify.js';
|
||||
|
||||
/**
|
||||
* Creates a new category under a given parent category.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that creates a category.
|
||||
*
|
||||
* @example
|
||||
* const fn = createUnderParent(pool);
|
||||
* const category = await fn({ googleAuthId: 42, parentId: 5, name: 'Drinks' });
|
||||
* // => { id, parentId, sort, name, slug }
|
||||
*/
|
||||
export default function createUnderParent(pool) {
|
||||
const access = resolveCategoryAccess(pool);
|
||||
|
||||
/**
|
||||
* Creates a new category under a given parent category.
|
||||
*
|
||||
* @param {object} input - The category creation input.
|
||||
* @param {number} input.googleAuthId - The id of the Google auth user.
|
||||
* @param {number} input.parentId - The id of the parent category.
|
||||
* @param {string} input.name - The category name.
|
||||
* @returns {Promise<{id: number, parentId: number, sort: number, name: string, slug: string}>} The created category.
|
||||
*
|
||||
* @example
|
||||
* const category = await store.category.createUnderParent({ googleAuthId: 42, parentId: 5, name: 'Drinks' });
|
||||
* // => { id, parentId, sort, name, slug }
|
||||
*/
|
||||
return async ({ googleAuthId, parentId, name }) => {
|
||||
const { languageId } = await access(googleAuthId, parentId);
|
||||
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import { DELETE } from '../../sql/category.js';
|
||||
import resolveCategoryAccess from './resolveCategoryAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes a category, refusing to delete the company's root category.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that deletes a category.
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn({ googleAuthId: 42, id: 5 });
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveCategoryAccess(pool);
|
||||
|
||||
/**
|
||||
* Deletes a category, refusing to delete the company's root category.
|
||||
*
|
||||
* @param {object} input - The delete input.
|
||||
* @param {number} input.googleAuthId - The id of the Google auth user.
|
||||
* @param {number} input.id - The id of the category to delete.
|
||||
* @returns {Promise<void>}
|
||||
* @throws {Error} If the category is the root category.
|
||||
*
|
||||
* @example
|
||||
* await store.category.delete({ googleAuthId: 42, id: 5 });
|
||||
*/
|
||||
return async ({ googleAuthId, id }) => {
|
||||
const { rootCategoryId } = await access(googleAuthId, id);
|
||||
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import { DELETE_BY_CATEGORY } from '../../sql/categorypic.js';
|
||||
import resolveCategoryAccess from './resolveCategoryAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes the image of a category.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that deletes a category image.
|
||||
*
|
||||
* @example
|
||||
* const fn = deleteImage(pool);
|
||||
* const result = await fn(42, 5);
|
||||
* // => { id, thumbW: null, thumbH: null, thumbHash: null }
|
||||
*/
|
||||
export default function deleteImage(pool) {
|
||||
const access = resolveCategoryAccess(pool);
|
||||
|
||||
/**
|
||||
* Deletes the image of a category.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} categoryId - The id of the category.
|
||||
* @returns {Promise<{id: number, thumbW: null, thumbH: null, thumbHash: null}>} The cleared image info.
|
||||
* @throws {Error} If the image is not found.
|
||||
*
|
||||
* @example
|
||||
* const result = await store.category.deleteImage(42, 5);
|
||||
* // => { id: 5, thumbW: null, thumbH: null, thumbHash: null }
|
||||
*/
|
||||
return async (googleAuthId, categoryId) => {
|
||||
await access(googleAuthId, categoryId);
|
||||
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import resolveCategoryAccess from './resolveCategoryAccess.js';
|
||||
import queryCategoryArticles from './queryCategoryArticles.js';
|
||||
|
||||
/**
|
||||
* Returns the articles belonging to a single category.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that fetches the articles of a category.
|
||||
*
|
||||
* @example
|
||||
* const fn = getCategoryArticles(pool);
|
||||
* const articles = await fn(42, 5);
|
||||
* // => [{ id, sku, name, ... }]
|
||||
*/
|
||||
export default function getCategoryArticles(pool) {
|
||||
const access = resolveCategoryAccess(pool);
|
||||
const articles = queryCategoryArticles(pool);
|
||||
|
||||
/**
|
||||
* Returns the articles belonging to a single category.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} categoryId - The id of the category.
|
||||
* @returns {Promise<Array<object>>} The articles in the category.
|
||||
*
|
||||
* @example
|
||||
* const articles = await store.category.getCategoryArticles(42, 5);
|
||||
* // => [{ id, sku, name, ... }]
|
||||
*/
|
||||
return async (googleAuthId, categoryId) => {
|
||||
const { languageId, posCustomerGroupId } = await access(
|
||||
googleAuthId,
|
||||
|
||||
@@ -3,23 +3,22 @@ import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyA
|
||||
import queryCategoryArticles from './queryCategoryArticles.js';
|
||||
import getCategoryPosterminalIds from '../posterminal/getCategoryPosterminalIds.js';
|
||||
|
||||
/**
|
||||
* Returns the full category tree for a company together with its articles and
|
||||
* the posterminal ids assigned to each category.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that fetches the category tree.
|
||||
*
|
||||
* @example
|
||||
* const fn = getTreeForCompany(pool);
|
||||
* const tree = await fn(42);
|
||||
* // => { languageId, tree: [{ id, posterminalIds, ... }], articles: [...] }
|
||||
*/
|
||||
export default function getTreeForCompany(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const articles = queryCategoryArticles(pool);
|
||||
const posterminalIds = getCategoryPosterminalIds(pool);
|
||||
|
||||
/**
|
||||
* Returns the full category tree for the user's selected company together
|
||||
* with its articles and the posterminal ids assigned to each category.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @returns {Promise<{languageId: number, tree: Array<object>, articles: Array<object>}>} The category tree, articles and language.
|
||||
*
|
||||
* @example
|
||||
* const tree = await store.category.getTreeForCompany(42);
|
||||
* // => { languageId, tree: [{ id, posterminalIds, ... }], articles: [...] }
|
||||
*/
|
||||
return async (googleAuthId) => {
|
||||
const { companyId, rootCategoryId, languageId, posCustomerGroupId } =
|
||||
await access(googleAuthId);
|
||||
|
||||
@@ -6,20 +6,23 @@ import {
|
||||
import request from '../request.js';
|
||||
import resolveCategoryAccess from './resolveCategoryAccess.js';
|
||||
|
||||
/**
|
||||
* Moves a category to a new parent, preventing cycles and cross-company moves.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that moves a category.
|
||||
*
|
||||
* @example
|
||||
* const fn = move(pool);
|
||||
* const result = await fn({ googleAuthId: 42, id: 5, parentId: 9 });
|
||||
* // => { id, parentId, sort }
|
||||
*/
|
||||
export default function move(pool) {
|
||||
const access = resolveCategoryAccess(pool);
|
||||
|
||||
/**
|
||||
* Moves a category to a new parent, preventing cycles and cross-company moves.
|
||||
*
|
||||
* @param {object} input - The move input.
|
||||
* @param {number} input.googleAuthId - The id of the Google auth user.
|
||||
* @param {number} input.id - The id of the category to move.
|
||||
* @param {number} input.parentId - The id of the new parent category.
|
||||
* @returns {Promise<{id: number, parentId: number, sort: number}>} The moved category.
|
||||
* @throws {Error} If the move would create a cycle or cross companies.
|
||||
*
|
||||
* @example
|
||||
* const result = await store.category.move({ googleAuthId: 42, id: 5, parentId: 9 });
|
||||
* // => { id, parentId, sort }
|
||||
*/
|
||||
return async ({ googleAuthId, id, parentId }) => {
|
||||
// Source category auth (throws if missing / not authorized).
|
||||
const source = await access(googleAuthId, id);
|
||||
|
||||
@@ -5,20 +5,23 @@ import {
|
||||
import request from '../request.js';
|
||||
import resolveCategoryAccess from './resolveCategoryAccess.js';
|
||||
|
||||
/**
|
||||
* Reorders the child categories of a given parent.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that reorders categories.
|
||||
*
|
||||
* @example
|
||||
* const fn = order(pool);
|
||||
* const result = await fn({ googleAuthId: 42, parentId: 5, categoryIds: [3, 1, 2] });
|
||||
* // => { categoryIds: [3, 1, 2] }
|
||||
*/
|
||||
export default function order(pool) {
|
||||
const access = resolveCategoryAccess(pool);
|
||||
|
||||
/**
|
||||
* Reorders the child categories of a given parent.
|
||||
*
|
||||
* @param {object} input - The reorder input.
|
||||
* @param {number} input.googleAuthId - The id of the Google auth user.
|
||||
* @param {number} input.parentId - The id of the parent category.
|
||||
* @param {Array<number>} input.categoryIds - The child category ids in their new order.
|
||||
* @returns {Promise<{categoryIds: Array<number>}>} The applied order.
|
||||
* @throws {Error} If the order is invalid.
|
||||
*
|
||||
* @example
|
||||
* const result = await store.category.order({ googleAuthId: 42, parentId: 5, categoryIds: [3, 1, 2] });
|
||||
* // => { categoryIds: [3, 1, 2] }
|
||||
*/
|
||||
return async ({ googleAuthId, parentId, categoryIds }) => {
|
||||
if (!Array.isArray(categoryIds)) {
|
||||
throw new Error('Invalid category order');
|
||||
|
||||
@@ -2,20 +2,23 @@ import { UPDATE_BY_CATEGORY_AND_LANGUAGE } from '../../sql/categorytext.js';
|
||||
import resolveCategoryAccess from './resolveCategoryAccess.js';
|
||||
import slugify from '../slugify.js';
|
||||
|
||||
/**
|
||||
* Renames a category and updates its slug.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that renames a category.
|
||||
*
|
||||
* @example
|
||||
* const fn = rename(pool);
|
||||
* const result = await fn({ googleAuthId: 42, id: 5, name: 'Soft Drinks' });
|
||||
* // => { id, name, slug }
|
||||
*/
|
||||
export default function rename(pool) {
|
||||
const access = resolveCategoryAccess(pool);
|
||||
|
||||
/**
|
||||
* Renames a category and updates its slug.
|
||||
*
|
||||
* @param {object} input - The rename input.
|
||||
* @param {number} input.googleAuthId - The id of the Google auth user.
|
||||
* @param {number} input.id - The id of the category to rename.
|
||||
* @param {string} input.name - The new category name.
|
||||
* @returns {Promise<{id: number, name: string, slug: string}>} The renamed category.
|
||||
* @throws {Error} If the name is empty or the category is not found.
|
||||
*
|
||||
* @example
|
||||
* const result = await store.category.rename({ googleAuthId: 42, id: 5, name: 'Soft Drinks' });
|
||||
* // => { id, name, slug }
|
||||
*/
|
||||
return async ({ googleAuthId, id, name }) => {
|
||||
if (!name?.trim()) {
|
||||
throw new Error('Name required');
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { DELETE_BY_COMPANY_AND_KEY } from '../../sql/companysetting.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes a company setting by key.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that deletes a company setting.
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 'theme');
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Deletes a company setting by key for the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {string} key - The setting key.
|
||||
* @returns {Promise<void>}
|
||||
* @throws {Error} If the setting is not found.
|
||||
*
|
||||
* @example
|
||||
* await store.companysetting.delete(42, 'theme');
|
||||
*/
|
||||
return async (googleAuthId, key) => {
|
||||
if (!key?.trim()) {
|
||||
throw new Error('Setting not found');
|
||||
|
||||
@@ -2,20 +2,22 @@ import { GET_BY_COMPANY_AND_KEY } from '../../sql/companysetting.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import shapeSetting from './shapeSetting.js';
|
||||
|
||||
/**
|
||||
* Returns a single company setting by key.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that fetches a company setting.
|
||||
*
|
||||
* @example
|
||||
* const fn = get(pool);
|
||||
* const setting = await fn(42, 'theme');
|
||||
* // => { key, value, frontend }
|
||||
*/
|
||||
export default function get(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Returns a single company setting by key for the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {string} key - The setting key.
|
||||
* @param {object} [options] - Optional options.
|
||||
* @param {boolean} [options.frontendOnly] - Only return the setting if it is frontend-visible.
|
||||
* @returns {Promise<{key: string, value: string, frontend: boolean}|undefined>} The setting, or `undefined` if not found.
|
||||
*
|
||||
* @example
|
||||
* const setting = await store.companysetting.get(42, 'theme');
|
||||
* // => { key, value, frontend }
|
||||
*/
|
||||
return async (googleAuthId, key, { frontendOnly = false } = {}) => {
|
||||
if (!key?.trim()) {
|
||||
return undefined;
|
||||
|
||||
@@ -5,20 +5,21 @@ import {
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import shapeSetting from './shapeSetting.js';
|
||||
|
||||
/**
|
||||
* Lists the settings of a company, optionally only frontend-visible ones.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that lists company settings.
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const settings = await fn(42, { frontendOnly: true });
|
||||
* // => [{ key, value, frontend }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Lists the settings of the user's selected company, optionally only frontend-visible ones.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {object} [options] - Optional options.
|
||||
* @param {boolean} [options.frontendOnly] - Only return frontend-visible settings.
|
||||
* @returns {Promise<Array<{key: string, value: string, frontend: boolean}>>} The settings.
|
||||
*
|
||||
* @example
|
||||
* const settings = await store.companysetting.list(42, { frontendOnly: true });
|
||||
* // => [{ key, value, frontend }]
|
||||
*/
|
||||
return async (googleAuthId, { frontendOnly = false } = {}) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
|
||||
@@ -2,20 +2,24 @@ import { GET_BY_COMPANY_AND_KEY, UPSERT } from '../../sql/companysetting.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import shapeSetting from './shapeSetting.js';
|
||||
|
||||
/**
|
||||
* Sets (upserts) a company setting by key.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that sets a company setting.
|
||||
*
|
||||
* @example
|
||||
* const fn = set(pool);
|
||||
* const setting = await fn(42, { key: 'theme', value: 'dark', frontend: true });
|
||||
* // => { key, value, frontend }
|
||||
*/
|
||||
export default function set(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Sets (upserts) a company setting by key for the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {object} input - The setting input.
|
||||
* @param {string} input.key - The setting key.
|
||||
* @param {string} input.value - The setting value.
|
||||
* @param {boolean} [input.frontend] - Whether the setting is frontend-visible.
|
||||
* @returns {Promise<{key: string, value: string, frontend: boolean}>} The upserted setting.
|
||||
* @throws {Error} If the input is invalid.
|
||||
*
|
||||
* @example
|
||||
* const setting = await store.companysetting.set(42, { key: 'theme', value: 'dark', frontend: true });
|
||||
* // => { key, value, frontend }
|
||||
*/
|
||||
return async (googleAuthId, input = {}) => {
|
||||
const key = input.key?.trim();
|
||||
if (!key) {
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { GET_BY_ID } from '../../sql/google_auth.js';
|
||||
import resolveCompanyAccess from './resolveCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Resolves the user's selected company and checks company membership.
|
||||
* @param {object} pool
|
||||
* @returns {(googleAuthId: number) => Promise<object>}
|
||||
*/
|
||||
export default function resolveSelectedCompanyAccess(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Resolves the user's selected company and checks company membership.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @returns {Promise<{companyId: number, rootCategoryId: number, languageId: number, posCustomerGroupId: number|null, companyuserId: number}>} The resolved company access context.
|
||||
* @throws {Error} If the user is not authorized or has no company selected.
|
||||
*/
|
||||
return async (googleAuthId) => {
|
||||
if (!Number.isFinite(googleAuthId)) {
|
||||
throw new Error('Not authorized');
|
||||
|
||||
@@ -8,20 +8,22 @@ function shape(row) {
|
||||
return { id: row.id, name: row.name, sort: row.sort };
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new customer group for a company.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that creates a customer group.
|
||||
*
|
||||
* @example
|
||||
* const fn = create(pool);
|
||||
* const group = await fn(42, { name: 'Wholesale' });
|
||||
* // => { id, name, sort }
|
||||
*/
|
||||
export default function create(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Creates a new customer group for the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {object} input - The customer group input.
|
||||
* @param {string} input.name - The customer group name (must not be a reserved name).
|
||||
* @returns {Promise<{id: number, name: string, sort: number}>} The created customer group.
|
||||
* @throws {Error} If the name is empty or reserved.
|
||||
*
|
||||
* @example
|
||||
* const group = await store.customergroup.create(42, { name: 'Wholesale' });
|
||||
* // => { id, name, sort }
|
||||
*/
|
||||
return async (googleAuthId, { name } = {}) => {
|
||||
const cleaned = name?.trim();
|
||||
if (!cleaned) {
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { DELETE_BY_COMPANY_AND_ID } from '../../sql/customergroup.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes a customer group for a company by its id.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that deletes a customer group.
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 5);
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Deletes a customer group for the user's selected company by its id.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} id - The id of the customer group to delete.
|
||||
* @returns {Promise<void>}
|
||||
* @throws {Error} If the customer group is not found.
|
||||
*
|
||||
* @example
|
||||
* await store.customergroup.delete(42, 5);
|
||||
*/
|
||||
return async (googleAuthId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Customer group not found');
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import { LIST_BY_COMPANY } from '../../sql/customergroup.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Lists the customer groups of a company.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that lists customer groups.
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const groups = await fn(42);
|
||||
* // => [{ id, name, sort }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Lists the customer groups of the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @returns {Promise<Array<{id: number, name: string, sort: number}>>} The customer groups.
|
||||
*
|
||||
* @example
|
||||
* const groups = await store.customergroup.list(42);
|
||||
* // => [{ id, name, sort }]
|
||||
*/
|
||||
return async (googleAuthId) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
|
||||
@@ -5,20 +5,22 @@ import {
|
||||
import request from '../request.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Reorders the customer groups of a company.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that reorders customer groups.
|
||||
*
|
||||
* @example
|
||||
* const fn = order(pool);
|
||||
* const result = await fn(42, { customergroupIds: [3, 1, 2] });
|
||||
* // => { customergroupIds: [3, 1, 2] }
|
||||
*/
|
||||
export default function order(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Reorders the customer groups of the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {object} input - The reorder input.
|
||||
* @param {Array<number>} input.customergroupIds - The customer group ids in their new order.
|
||||
* @returns {Promise<{customergroupIds: Array<number>}>} The applied order.
|
||||
* @throws {Error} If the order is invalid.
|
||||
*
|
||||
* @example
|
||||
* const result = await store.customergroup.order(42, { customergroupIds: [3, 1, 2] });
|
||||
* // => { customergroupIds: [3, 1, 2] }
|
||||
*/
|
||||
return async (googleAuthId, { customergroupIds } = {}) => {
|
||||
if (!Array.isArray(customergroupIds)) {
|
||||
throw new Error('Invalid customer group order');
|
||||
|
||||
@@ -8,20 +8,23 @@ function shape(row) {
|
||||
return { id: row.id, name: row.name, sort: row.sort };
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the name of a customer group for a company.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that updates a customer group.
|
||||
*
|
||||
* @example
|
||||
* const fn = update(pool);
|
||||
* const group = await fn(42, 5, { name: 'Retail' });
|
||||
* // => { id, name, sort }
|
||||
*/
|
||||
export default function update(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Updates the name of a customer group for the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} id - The id of the customer group to update.
|
||||
* @param {object} input - The update input.
|
||||
* @param {string} input.name - The new customer group name.
|
||||
* @returns {Promise<{id: number, name: string, sort: number}>} The updated customer group.
|
||||
* @throws {Error} If the customer group is not found or the name is empty.
|
||||
*
|
||||
* @example
|
||||
* const group = await store.customergroup.update(42, 5, { name: 'Retail' });
|
||||
* // => { id, name, sort }
|
||||
*/
|
||||
return async (googleAuthId, id, { name } = {}) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Customer group not found');
|
||||
|
||||
@@ -2,31 +2,33 @@ import { CREATE_MANIFEST, DELIVERY_TYPE } from '../../sql/delivery.js';
|
||||
import request from '../request.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import taxEnsure from '../tax/ensure.js';
|
||||
import {
|
||||
loadDelivery,
|
||||
normalizeLines,
|
||||
parseDeliveryDate,
|
||||
} from './helpers.js';
|
||||
import { assertSupplier, insertLines } from './writeLines.js';
|
||||
import { insertLines, normalizeLines, parseDate } from '../manifestLines.js';
|
||||
import { loadDelivery } from './helpers.js';
|
||||
import { assertSupplier } from './writeLines.js';
|
||||
|
||||
/**
|
||||
* Creates a new delivery for a company with its lines.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that creates a delivery.
|
||||
*
|
||||
* @example
|
||||
* const fn = create(pool);
|
||||
* const delivery = await fn(42, { number: 'D-1', date: '2026-01-01', supplierId: 5, lines: [...] });
|
||||
* // => { id, number, type, entityId, date, lines: [...] }
|
||||
*/
|
||||
export default function create(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const ensureTax = taxEnsure(pool);
|
||||
|
||||
/**
|
||||
* Creates a new delivery for the user's selected company with its lines.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {object} body - The delivery input.
|
||||
* @param {string} body.number - The delivery number.
|
||||
* @param {string} body.date - The delivery date.
|
||||
* @param {number} body.supplierId - The id of the supplier.
|
||||
* @param {Array<object>} [body.lines] - The delivery lines.
|
||||
* @returns {Promise<{id: number, number: string, type: string, entityId: number, date: string, lines: Array<object>}>} The created delivery.
|
||||
* @throws {Error} If the delivery is invalid.
|
||||
*
|
||||
* @example
|
||||
* const delivery = await store.delivery.create(42, { number: 'D-1', date: '2026-01-01', supplierId: 5, lines: [...] });
|
||||
* // => { id, number, type, entityId, date, lines: [...] }
|
||||
*/
|
||||
return async (googleAuthId, body = {}) => {
|
||||
const number = body.number?.trim?.() ? body.number.trim() : null;
|
||||
const date = parseDeliveryDate(body.date);
|
||||
const date = parseDate(body.date);
|
||||
if (!number || !date) {
|
||||
throw new Error('Invalid delivery');
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { DELETE_MANIFEST, DELIVERY_TYPE } from '../../sql/delivery.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes a delivery by its id within a company.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that deletes a delivery.
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 5);
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Deletes a delivery by its id within the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} id - The id of the delivery to delete.
|
||||
* @returns {Promise<void>}
|
||||
* @throws {Error} If the delivery is not found.
|
||||
*
|
||||
* @example
|
||||
* await store.delivery.delete(42, 5);
|
||||
*/
|
||||
return async (googleAuthId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Delivery not found');
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import { loadDelivery } from './helpers.js';
|
||||
|
||||
/**
|
||||
* Returns a single delivery by its id within a company.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that fetches a delivery by id.
|
||||
*
|
||||
* @example
|
||||
* const fn = getById(pool);
|
||||
* const delivery = await fn(42, 5);
|
||||
* // => { id, number, type, entityId, date, lines: [...] }
|
||||
*/
|
||||
export default function getById(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Returns a single delivery by its id within the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} id - The id of the delivery.
|
||||
* @returns {Promise<{id: number, number: string, type: string, entityId: number, date: string, lines: Array<object>}>} The delivery.
|
||||
* @throws {Error} If the delivery is not found.
|
||||
*
|
||||
* @example
|
||||
* const delivery = await store.delivery.getById(42, 5);
|
||||
* // => { id, number, type, entityId, date, lines: [...] }
|
||||
*/
|
||||
return async (googleAuthId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Delivery not found');
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
GET_DELIVERY_LINES,
|
||||
} from '../../sql/delivery.js';
|
||||
import request from '../request.js';
|
||||
import { shapeLine } from '../manifestLines.js';
|
||||
|
||||
export function shapeSummary(row) {
|
||||
return {
|
||||
@@ -15,19 +16,6 @@ export function shapeSummary(row) {
|
||||
};
|
||||
}
|
||||
|
||||
export function shapeLine(row) {
|
||||
return {
|
||||
id: row.id,
|
||||
articleId: row.articleId ?? null,
|
||||
sku: row.sku ?? null,
|
||||
amount: row.amount,
|
||||
net: row.net,
|
||||
rebate: row.rebate,
|
||||
tax: row.tax,
|
||||
comment: row.comment ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadDelivery(pool, tx, companyId, id) {
|
||||
const header = await request(pool, tx)
|
||||
.input('id', id)
|
||||
@@ -49,50 +37,3 @@ export async function loadDelivery(pool, tx, companyId, id) {
|
||||
lines: lines.recordset.map(shapeLine),
|
||||
};
|
||||
}
|
||||
|
||||
export function parseDeliveryDate(value) {
|
||||
if (value instanceof Date && !Number.isNaN(value.getTime())) {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === 'string' && value.trim()) {
|
||||
const d = new Date(value);
|
||||
if (!Number.isNaN(d.getTime())) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function normalizeLines(lines) {
|
||||
if (!Array.isArray(lines)) {
|
||||
throw new Error('Invalid lines');
|
||||
}
|
||||
|
||||
return lines.map((entry) => {
|
||||
const sku = entry?.sku?.trim?.() ? entry.sku.trim() : null;
|
||||
if (!sku) {
|
||||
throw new Error('Invalid lines');
|
||||
}
|
||||
|
||||
const amount = entry?.amount;
|
||||
const net = entry?.net;
|
||||
const rebate =
|
||||
entry?.rebate === undefined || entry?.rebate === null ? 0 : entry.rebate;
|
||||
const tax = entry?.tax;
|
||||
const comment =
|
||||
entry?.comment === undefined || entry?.comment === null
|
||||
? null
|
||||
: String(entry.comment);
|
||||
|
||||
if (
|
||||
!Number.isFinite(amount) ||
|
||||
!Number.isFinite(net) ||
|
||||
!Number.isFinite(rebate) ||
|
||||
!Number.isFinite(tax)
|
||||
) {
|
||||
throw new Error('Invalid lines');
|
||||
}
|
||||
|
||||
return { sku, amount, net, rebate, tax, comment };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,20 +2,19 @@ import { DELIVERY_TYPE, LIST_DELIVERIES } from '../../sql/delivery.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import { shapeSummary } from './helpers.js';
|
||||
|
||||
/**
|
||||
* Lists the deliveries of a company.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that lists deliveries.
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const deliveries = await fn(42);
|
||||
* // => [{ id, number, type, entityId, date }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Lists the deliveries of the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @returns {Promise<Array<{id: number, number: string, type: string, entityId: number, date: string}>>} The deliveries.
|
||||
*
|
||||
* @example
|
||||
* const deliveries = await store.delivery.list(42);
|
||||
* // => [{ id, number, type, entityId, date }]
|
||||
*/
|
||||
return async (googleAuthId) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
|
||||
@@ -7,29 +7,31 @@ import {
|
||||
import request from '../request.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import taxEnsure from '../tax/ensure.js';
|
||||
import {
|
||||
loadDelivery,
|
||||
normalizeLines,
|
||||
parseDeliveryDate,
|
||||
shapeSummary,
|
||||
} from './helpers.js';
|
||||
import { assertSupplier, insertLines } from './writeLines.js';
|
||||
import { insertLines, normalizeLines, parseDate } from '../manifestLines.js';
|
||||
import { loadDelivery, shapeSummary } from './helpers.js';
|
||||
import { assertSupplier } from './writeLines.js';
|
||||
|
||||
/**
|
||||
* Updates a delivery's header and/or lines.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that updates a delivery.
|
||||
*
|
||||
* @example
|
||||
* const fn = update(pool);
|
||||
* const delivery = await fn(42, 5, { number: 'D-2', lines: [...] });
|
||||
* // => { id, number, type, entityId, date, lines: [...] }
|
||||
*/
|
||||
export default function update(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const ensureTax = taxEnsure(pool);
|
||||
|
||||
/**
|
||||
* Updates a delivery's header and/or lines.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} id - The id of the delivery to update.
|
||||
* @param {object} patch - The update patch.
|
||||
* @param {string} [patch.number] - The new delivery number.
|
||||
* @param {string} [patch.date] - The new delivery date.
|
||||
* @param {number} [patch.supplierId] - The new supplier id.
|
||||
* @param {Array<object>} [patch.lines] - The new delivery lines.
|
||||
* @returns {Promise<{id: number, number: string, type: string, entityId: number, date: string, lines: Array<object>}>} The updated delivery.
|
||||
* @throws {Error} If the delivery is not found.
|
||||
*
|
||||
* @example
|
||||
* const delivery = await store.delivery.update(42, 5, { number: 'D-2', lines: [...] });
|
||||
* // => { id, number, type, entityId, date, lines: [...] }
|
||||
*/
|
||||
return async (googleAuthId, id, patch = {}) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Delivery not found');
|
||||
@@ -81,7 +83,7 @@ export default function update(pool) {
|
||||
|
||||
let date = summary.date;
|
||||
if (hasDate) {
|
||||
date = parseDeliveryDate(patch.date);
|
||||
date = parseDate(patch.date);
|
||||
if (!date) {
|
||||
throw new Error('Invalid delivery');
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import {
|
||||
CREATE_LINE,
|
||||
RESOLVE_ARTICLE_ID_BY_SKU,
|
||||
} from '../../sql/delivery.js';
|
||||
import { GET_SUPPLIER_BY_ID, SUPPLIER_GROUP_NAME } from '../../sql/entity.js';
|
||||
import request from '../request.js';
|
||||
|
||||
@@ -24,42 +20,3 @@ export async function assertSupplier(pool, tx, companyId, supplierId) {
|
||||
}
|
||||
return supplierId;
|
||||
}
|
||||
|
||||
async function resolveArticleId(pool, tx, rootCategoryId, sku) {
|
||||
const result = await request(pool, tx)
|
||||
.input('rootCategoryId', rootCategoryId)
|
||||
.input('sku', sku)
|
||||
.query(RESOLVE_ARTICLE_ID_BY_SKU);
|
||||
|
||||
const articleId = result.recordset[0]?.id;
|
||||
if (!articleId) {
|
||||
throw new Error('Article not found');
|
||||
}
|
||||
return articleId;
|
||||
}
|
||||
|
||||
export async function insertLines(
|
||||
pool,
|
||||
tx,
|
||||
{ manifestId, rootCategoryId, lines, ensureTax },
|
||||
) {
|
||||
for (const line of lines) {
|
||||
const articleId = await resolveArticleId(
|
||||
pool,
|
||||
tx,
|
||||
rootCategoryId,
|
||||
line.sku,
|
||||
);
|
||||
const taxId = await ensureTax(line.tax, tx);
|
||||
|
||||
await request(pool, tx)
|
||||
.input('manifestId', manifestId)
|
||||
.input('articleId', articleId)
|
||||
.input('amount', line.amount)
|
||||
.input('net', line.net)
|
||||
.input('comment', line.comment)
|
||||
.input('rebate', line.rebate)
|
||||
.input('taxId', taxId)
|
||||
.query(CREATE_LINE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,24 +6,29 @@ import resolveCustomerGroupAccess from '../customergroup/resolveCustomerGroupAcc
|
||||
import nextNumber from '../numbers/nextNumber.js';
|
||||
import shape from './shape.js';
|
||||
|
||||
/**
|
||||
* Creates a new entity (customer) in a customer group, recording history.
|
||||
* Optional `number`; if omitted, allocates via `getnextnumberwithprefixandpadding`
|
||||
* (`type` `entity`).
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that creates an entity.
|
||||
*
|
||||
* @example
|
||||
* const fn = create(pool);
|
||||
* const entity = await fn(42, { customergroupId: 5, name: 'Doe', firstName: 'John' });
|
||||
* // => { id, customergroupId, firstName, name, number, createdAt, updatedAt }
|
||||
*/
|
||||
export default function create(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const groupAccess = resolveCustomerGroupAccess(pool);
|
||||
const allocateNumber = nextNumber(pool);
|
||||
|
||||
/**
|
||||
* Creates a new entity (customer) in a customer group, recording history.
|
||||
* Optional `number`; if omitted, allocates via `getnextnumberwithprefixandpadding`
|
||||
* (`type` `entity`).
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {object} input - The entity creation input.
|
||||
* @param {number} input.customergroupId - The id of the customer group.
|
||||
* @param {string} input.name - The entity name.
|
||||
* @param {string} [input.firstName] - The entity first name.
|
||||
* @param {string} [input.number] - Optional explicit customer number.
|
||||
* @returns {Promise<{id: number, customergroupId: number, firstName: string|null, name: string, number: string, createdAt: string, updatedAt: string}>} The created entity.
|
||||
* @throws {Error} If the name is empty.
|
||||
*
|
||||
* @example
|
||||
* const entity = await store.entity.create(42, { customergroupId: 5, name: 'Doe', firstName: 'John' });
|
||||
* // => { id, customergroupId, firstName, name, number, createdAt, updatedAt }
|
||||
*/
|
||||
return async (googleAuthId, { customergroupId, name, firstName = null, number } = {}) => {
|
||||
const cleaned = name?.trim();
|
||||
if (!cleaned) {
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { DELETE_BY_COMPANY_AND_ID } from '../../sql/entity.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes an entity by its id within a company.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that deletes an entity.
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 5);
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Deletes an entity by its id within the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} id - The id of the entity to delete.
|
||||
* @returns {Promise<void>}
|
||||
* @throws {Error} If the entity is not found.
|
||||
*
|
||||
* @example
|
||||
* await store.entity.delete(42, 5);
|
||||
*/
|
||||
return async (googleAuthId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Entity not found');
|
||||
|
||||
@@ -2,20 +2,21 @@ import { GET_BY_COMPANY_AND_ID } from '../../sql/entity.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import shape from './shape.js';
|
||||
|
||||
/**
|
||||
* Returns a single entity by its id within a company.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that fetches an entity by id.
|
||||
*
|
||||
* @example
|
||||
* const fn = getById(pool);
|
||||
* const entity = await fn(42, 5);
|
||||
* // => { id, customergroupId, firstName, name, number, createdAt, updatedAt }
|
||||
*/
|
||||
export default function getById(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Returns a single entity by its id within the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} id - The id of the entity.
|
||||
* @returns {Promise<{id: number, customergroupId: number, firstName: string|null, name: string, number: string, createdAt: string, updatedAt: string}>} The entity.
|
||||
* @throws {Error} If the entity is not found.
|
||||
*
|
||||
* @example
|
||||
* const entity = await store.entity.getById(42, 5);
|
||||
* // => { id, customergroupId, firstName, name, number, createdAt, updatedAt }
|
||||
*/
|
||||
return async (googleAuthId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Entity not found');
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
import { NUMBER_TAKEN_IN_COMPANY } from '../../sql/entity.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Returns whether a customer number is free within the company.
|
||||
* Pass `excludeEntityId` when editing so the entity's own number counts as free.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that checks number availability.
|
||||
*
|
||||
* @example
|
||||
* const fn = isNumberFree(pool);
|
||||
* const free = await fn(42, 'C-100');
|
||||
* // => true
|
||||
* const freeOnEdit = await fn(42, 'C-100', { excludeEntityId: 5 });
|
||||
* // => true
|
||||
*/
|
||||
export default function isNumberFree(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Returns whether a customer number is free within the user's selected company.
|
||||
* Pass `excludeEntityId` when editing so the entity's own number counts as free.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {string} number - The customer number to check.
|
||||
* @param {object} [options] - Optional options.
|
||||
* @param {number} [options.excludeEntityId] - An entity id to exclude from the check.
|
||||
* @returns {Promise<boolean>} Whether the number is free.
|
||||
* @throws {Error} If the number is empty.
|
||||
*
|
||||
* @example
|
||||
* const free = await store.entity.isNumberFree(42, 'C-100');
|
||||
* // => true
|
||||
* const freeOnEdit = await store.entity.isNumberFree(42, 'C-100', { excludeEntityId: 5 });
|
||||
* // => true
|
||||
*/
|
||||
return async (googleAuthId, number, { excludeEntityId } = {}) => {
|
||||
const cleaned =
|
||||
number == null || number === '' ? null : String(number).trim() || null;
|
||||
|
||||
@@ -9,22 +9,25 @@ import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyA
|
||||
import resolveCustomerGroupAccess from '../customergroup/resolveCustomerGroupAccess.js';
|
||||
import shape from './shape.js';
|
||||
|
||||
/**
|
||||
* Lists the entities (customers) of a company, optionally filtered by customer
|
||||
* group or restricted to the 'customer' role.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that lists entities.
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const entities = await fn(42, { customergroupId: 5 });
|
||||
* // => [{ id, customergroupId, firstName, name, number, createdAt, updatedAt }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const groupAccess = resolveCustomerGroupAccess(pool);
|
||||
|
||||
/**
|
||||
* Lists the entities (customers) of the user's selected company, optionally
|
||||
* filtered by customer group or restricted to the 'customer' role.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {object} [options] - Optional options.
|
||||
* @param {number} [options.customergroupId] - Filter by customer group id.
|
||||
* @param {string} [options.role] - Restrict to `'customer'` role if set.
|
||||
* @returns {Promise<Array<{id: number, customergroupId: number, firstName: string|null, name: string, number: string, createdAt: string, updatedAt: string}>>} The entities.
|
||||
* @throws {Error} If the role is invalid.
|
||||
*
|
||||
* @example
|
||||
* const entities = await store.entity.list(42, { customergroupId: 5 });
|
||||
* // => [{ id, customergroupId, firstName, name, number, createdAt, updatedAt }]
|
||||
*/
|
||||
return async (googleAuthId, { customergroupId, role } = {}) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import peekNumber from '../numbers/peekNumber.js';
|
||||
|
||||
/**
|
||||
* Returns the next customer number that would be allocated for the company
|
||||
* (`numbers` type `entity`) without consuming it.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that peeks the next entity number.
|
||||
*
|
||||
* @example
|
||||
* const fn = peekNextNumber(pool);
|
||||
* const number = await fn(42);
|
||||
* // => '1'
|
||||
*/
|
||||
export default function peekNextNumber(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const peek = peekNumber(pool);
|
||||
|
||||
/**
|
||||
* Returns the next customer number that would be allocated for the user's
|
||||
* selected company (`numbers` type `entity`) without consuming it.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @returns {Promise<string>} The next entity number.
|
||||
*
|
||||
* @example
|
||||
* const number = await store.entity.peekNextNumber(42);
|
||||
* // => '1'
|
||||
*/
|
||||
return async (googleAuthId) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
return peek(companyId, 'entity');
|
||||
|
||||
@@ -4,21 +4,26 @@ import request from '../request.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import shape from './shape.js';
|
||||
|
||||
/**
|
||||
* Updates name, first name, and/or customer number of an entity, recording history.
|
||||
* Optional `number` sets the customer number explicitly (does not auto-allocate).
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that updates an entity.
|
||||
*
|
||||
* @example
|
||||
* const fn = update(pool);
|
||||
* const entity = await fn(42, 5, { name: 'Doe', firstName: 'Jane', number: 'C-42' });
|
||||
* // => { id, customergroupId, firstName, name, number, createdAt, updatedAt }
|
||||
*/
|
||||
export default function update(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Updates name, first name, and/or customer number of an entity, recording history.
|
||||
* Optional `number` sets the customer number explicitly (does not auto-allocate).
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} id - The id of the entity to update.
|
||||
* @param {object} patch - The update patch.
|
||||
* @param {string} [patch.name] - The new entity name.
|
||||
* @param {string} [patch.firstName] - The new first name.
|
||||
* @param {string} [patch.number] - The new customer number.
|
||||
* @returns {Promise<{id: number, customergroupId: number, firstName: string|null, name: string, number: string, createdAt: string, updatedAt: string}>} The updated entity.
|
||||
* @throws {Error} If no valid field is provided or the entity is not found.
|
||||
*
|
||||
* @example
|
||||
* const entity = await store.entity.update(42, 5, { name: 'Doe', firstName: 'Jane', number: 'C-42' });
|
||||
* // => { id, customergroupId, firstName, name, number, createdAt, updatedAt }
|
||||
*/
|
||||
return async (googleAuthId, id, patch = {}) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Entity not found');
|
||||
|
||||
@@ -24,21 +24,32 @@ function optionalString(value) {
|
||||
return s || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new address for an entity.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that creates an entity address.
|
||||
*
|
||||
* @example
|
||||
* const fn = create(pool);
|
||||
* const address = await fn(42, 5, { addressType: 'billing', street: 'Main St', city: 'Berlin' });
|
||||
* // => { id, entityId, firstName, name, street, streetNumber, zip, city, county, addressType, sort }
|
||||
*/
|
||||
export default function create(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const entityAccess = resolveEntityAccess(pool);
|
||||
|
||||
/**
|
||||
* Creates a new address for an entity.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} entityId - The id of the entity.
|
||||
* @param {object} input - The address input.
|
||||
* @param {string} input.addressType - The address type (e.g. `'billing'`).
|
||||
* @param {string} [input.firstName] - The first name.
|
||||
* @param {string} [input.name] - The name.
|
||||
* @param {string} [input.street] - The street.
|
||||
* @param {string} [input.streetNumber] - The street number.
|
||||
* @param {string} [input.zip] - The zip code.
|
||||
* @param {string} [input.city] - The city.
|
||||
* @param {string} [input.county] - The county.
|
||||
* @param {number} [input.sort] - Optional sort order.
|
||||
* @returns {Promise<object>} The created address.
|
||||
* @throws {Error} If the address type is missing.
|
||||
*
|
||||
* @example
|
||||
* const address = await store.entityaddress.create(42, 5, { addressType: 'billing', street: 'Main St', city: 'Berlin' });
|
||||
* // => { id, entityId, firstName, name, street, streetNumber, zip, city, county, addressType, sort }
|
||||
*/
|
||||
return async (googleAuthId, entityId, input = {}) => {
|
||||
const addressType = input.addressType?.trim?.()
|
||||
? input.addressType.trim()
|
||||
|
||||
@@ -2,20 +2,22 @@ import { DELETE_BY_ENTITY_AND_ID } from '../../sql/entityaddress.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import resolveEntityAccess from '../entity/resolveEntityAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes an address of an entity.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that deletes an entity address.
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 5, 9);
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const entityAccess = resolveEntityAccess(pool);
|
||||
|
||||
/**
|
||||
* Deletes an address of an entity.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} entityId - The id of the entity.
|
||||
* @param {number} id - The id of the address to delete.
|
||||
* @returns {Promise<void>}
|
||||
* @throws {Error} If the address is not found.
|
||||
*
|
||||
* @example
|
||||
* await store.entityaddress.delete(42, 5, 9);
|
||||
*/
|
||||
return async (googleAuthId, entityId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Address not found');
|
||||
|
||||
@@ -18,21 +18,21 @@ function shape(row) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the addresses of an entity.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that lists entity addresses.
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const addresses = await fn(42, 5);
|
||||
* // => [{ id, entityId, firstName, name, street, streetNumber, zip, city, county, addressType, sort }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const entityAccess = resolveEntityAccess(pool);
|
||||
|
||||
/**
|
||||
* Lists the addresses of an entity.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} entityId - The id of the entity.
|
||||
* @returns {Promise<Array<object>>} The addresses.
|
||||
*
|
||||
* @example
|
||||
* const addresses = await store.entityaddress.list(42, 5);
|
||||
* // => [{ id, entityId, firstName, name, street, streetNumber, zip, city, county, addressType, sort }]
|
||||
*/
|
||||
return async (googleAuthId, entityId) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
await entityAccess(companyId, entityId);
|
||||
|
||||
@@ -24,21 +24,24 @@ function optionalString(value) {
|
||||
return s || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an address of an entity.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that updates an entity address.
|
||||
*
|
||||
* @example
|
||||
* const fn = update(pool);
|
||||
* const address = await fn(42, 5, 9, { city: 'Hamburg' });
|
||||
* // => { id, entityId, firstName, name, street, streetNumber, zip, city, county, addressType, sort }
|
||||
*/
|
||||
export default function update(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const entityAccess = resolveEntityAccess(pool);
|
||||
|
||||
/**
|
||||
* Updates an address of an entity.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} entityId - The id of the entity.
|
||||
* @param {number} id - The id of the address to update.
|
||||
* @param {object} patch - The update patch (any subset of address fields).
|
||||
* @returns {Promise<object>} The updated address.
|
||||
* @throws {Error} If the address is not found.
|
||||
*
|
||||
* @example
|
||||
* const address = await store.entityaddress.update(42, 5, 9, { city: 'Hamburg' });
|
||||
* // => { id, entityId, firstName, name, street, streetNumber, zip, city, county, addressType, sort }
|
||||
*/
|
||||
return async (googleAuthId, entityId, id, patch = {}) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Address not found');
|
||||
|
||||
@@ -12,21 +12,26 @@ function shape(row) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new contact for an entity.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that creates an entity contact.
|
||||
*
|
||||
* @example
|
||||
* const fn = create(pool);
|
||||
* const contact = await fn(42, 5, { contactType: 'email', contact: 'a@b.c' });
|
||||
* // => { id, entityId, contactType, contact, sort }
|
||||
*/
|
||||
export default function create(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const entityAccess = resolveEntityAccess(pool);
|
||||
|
||||
/**
|
||||
* Creates a new contact for an entity.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} entityId - The id of the entity.
|
||||
* @param {object} input - The contact input.
|
||||
* @param {string} input.contactType - The contact type (e.g. `'email'`).
|
||||
* @param {string} input.contact - The contact value.
|
||||
* @param {number} [input.sort] - Optional sort order.
|
||||
* @returns {Promise<{id: number, entityId: number, contactType: string, contact: string, sort: number}>} The created contact.
|
||||
* @throws {Error} If the contact type or value is missing.
|
||||
*
|
||||
* @example
|
||||
* const contact = await store.entitycontact.create(42, 5, { contactType: 'email', contact: 'a@b.c' });
|
||||
* // => { id, entityId, contactType, contact, sort }
|
||||
*/
|
||||
return async (googleAuthId, entityId, input = {}) => {
|
||||
const contactType = input.contactType?.trim?.()
|
||||
? input.contactType.trim()
|
||||
|
||||
@@ -2,20 +2,22 @@ import { DELETE_BY_ENTITY_AND_ID } from '../../sql/entitycontact.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import resolveEntityAccess from '../entity/resolveEntityAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes a contact of an entity.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that deletes an entity contact.
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 5, 9);
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const entityAccess = resolveEntityAccess(pool);
|
||||
|
||||
/**
|
||||
* Deletes a contact of an entity.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} entityId - The id of the entity.
|
||||
* @param {number} id - The id of the contact to delete.
|
||||
* @returns {Promise<void>}
|
||||
* @throws {Error} If the contact is not found.
|
||||
*
|
||||
* @example
|
||||
* await store.entitycontact.delete(42, 5, 9);
|
||||
*/
|
||||
return async (googleAuthId, entityId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Contact not found');
|
||||
|
||||
@@ -12,21 +12,21 @@ function shape(row) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the contacts of an entity.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that lists entity contacts.
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const contacts = await fn(42, 5);
|
||||
* // => [{ id, entityId, contactType, contact, sort }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const entityAccess = resolveEntityAccess(pool);
|
||||
|
||||
/**
|
||||
* Lists the contacts of an entity.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} entityId - The id of the entity.
|
||||
* @returns {Promise<Array<{id: number, entityId: number, contactType: string, contact: string, sort: number}>>} The contacts.
|
||||
*
|
||||
* @example
|
||||
* const contacts = await store.entitycontact.list(42, 5);
|
||||
* // => [{ id, entityId, contactType, contact, sort }]
|
||||
*/
|
||||
return async (googleAuthId, entityId) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
await entityAccess(companyId, entityId);
|
||||
|
||||
@@ -12,21 +12,24 @@ function shape(row) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a contact of an entity.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that updates an entity contact.
|
||||
*
|
||||
* @example
|
||||
* const fn = update(pool);
|
||||
* const contact = await fn(42, 5, 9, { contact: 'b@c.d' });
|
||||
* // => { id, entityId, contactType, contact, sort }
|
||||
*/
|
||||
export default function update(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const entityAccess = resolveEntityAccess(pool);
|
||||
|
||||
/**
|
||||
* Updates a contact of an entity.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} entityId - The id of the entity.
|
||||
* @param {number} id - The id of the contact to update.
|
||||
* @param {object} patch - The update patch (any subset of contact fields).
|
||||
* @returns {Promise<{id: number, entityId: number, contactType: string, contact: string, sort: number}>} The updated contact.
|
||||
* @throws {Error} If the contact is not found.
|
||||
*
|
||||
* @example
|
||||
* const contact = await store.entitycontact.update(42, 5, 9, { contact: 'b@c.d' });
|
||||
* // => { id, entityId, contactType, contact, sort }
|
||||
*/
|
||||
return async (googleAuthId, entityId, id, patch = {}) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Contact not found');
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { ADD } from '../../sql/google_auth.js';
|
||||
|
||||
/**
|
||||
* Adds a new allowed Google auth email.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that adds an allowed email.
|
||||
*
|
||||
* @example
|
||||
* const fn = add(pool);
|
||||
* const id = await fn('user@example.com');
|
||||
* // => 1
|
||||
*/
|
||||
export default function add(pool) {
|
||||
/**
|
||||
* Adds a new allowed Google auth email.
|
||||
*
|
||||
* @param {string} email - The email address to allow.
|
||||
* @returns {Promise<number>} The id of the created Google auth row.
|
||||
*
|
||||
* @example
|
||||
* const id = await store.google_auth.add('user@example.com');
|
||||
* // => 1
|
||||
*/
|
||||
return async (email) => {
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { GET_BY_EMAIL } from '../../sql/google_auth.js';
|
||||
|
||||
/**
|
||||
* Returns a Google auth row by email.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that fetches a Google auth row by email.
|
||||
*
|
||||
* @example
|
||||
* const fn = getByEmail(pool);
|
||||
* const auth = await fn('user@example.com');
|
||||
* // => { id, email, ... }
|
||||
*/
|
||||
export default function getByEmail(pool) {
|
||||
/**
|
||||
* Returns a Google auth row by email.
|
||||
*
|
||||
* @param {string} email - The email address.
|
||||
* @returns {Promise<object|undefined>} The Google auth row, or `undefined` if not found.
|
||||
*
|
||||
* @example
|
||||
* const auth = await store.google_auth.getByEmail('user@example.com');
|
||||
* // => { id, email, ... }
|
||||
*/
|
||||
return async (email) => {
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { GET_BY_ID } from '../../sql/google_auth.js';
|
||||
|
||||
/**
|
||||
* Returns a Google auth row by its id.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that fetches a Google auth row by id.
|
||||
*
|
||||
* @example
|
||||
* const fn = getById(pool);
|
||||
* const auth = await fn(42);
|
||||
* // => { id, email, ... }
|
||||
*/
|
||||
export default function getById(pool) {
|
||||
/**
|
||||
* Returns a Google auth row by its id.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth row.
|
||||
* @returns {Promise<object|undefined>} The Google auth row, or `undefined` if not found.
|
||||
*
|
||||
* @example
|
||||
* const auth = await store.google_auth.getById(42);
|
||||
* // => { id, email, ... }
|
||||
*/
|
||||
return async (googleAuthId) => {
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import { LIST_ALLOWED_EMAILS } from '../../sql/google_auth.js';
|
||||
|
||||
/**
|
||||
* Lists all allowed Google auth emails.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that lists allowed emails.
|
||||
*
|
||||
* @example
|
||||
* const fn = listAllowedEmails(pool);
|
||||
* const emails = await fn();
|
||||
* // => [{ id, email }]
|
||||
*/
|
||||
export default function listAllowedEmails(pool) {
|
||||
/**
|
||||
* Lists all allowed Google auth emails.
|
||||
*
|
||||
* @returns {Promise<Array<{id: number, email: string}>>} The allowed emails.
|
||||
*
|
||||
* @example
|
||||
* const emails = await store.google_auth.listAllowedEmails();
|
||||
* // => [{ id, email }]
|
||||
*/
|
||||
return async () => {
|
||||
const result = await pool.request().query(LIST_ALLOWED_EMAILS);
|
||||
return result.recordset;
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { REMOVE_ALLOWED_EMAIL } from '../../sql/google_auth.js';
|
||||
|
||||
/**
|
||||
* Removes an allowed Google auth email.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that removes an allowed email.
|
||||
*
|
||||
* @example
|
||||
* const fn = removeAllowedEmail(pool);
|
||||
* await fn('user@example.com');
|
||||
*/
|
||||
export default function removeAllowedEmail(pool) {
|
||||
/**
|
||||
* Removes an allowed Google auth email.
|
||||
*
|
||||
* @param {string} email - The email address to remove.
|
||||
* @returns {Promise<void>}
|
||||
*
|
||||
* @example
|
||||
* await store.google_auth.removeAllowedEmail('user@example.com');
|
||||
*/
|
||||
return async (email) => {
|
||||
await pool
|
||||
.request()
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
import { SET_PROFILE } from '../../sql/google_auth.js';
|
||||
|
||||
/**
|
||||
* Updates the profile (picture, picture binary, sub) of a Google auth user.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that sets the user profile.
|
||||
*
|
||||
* @example
|
||||
* const fn = setProfile(pool);
|
||||
* await fn(42, { picture: 'url', pictureBinary: buffer, sub: 'sub' });
|
||||
*/
|
||||
export default function setProfile(pool) {
|
||||
/**
|
||||
* Updates the profile (picture, picture binary, sub) of a Google auth user.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {object} input - The profile input.
|
||||
* @param {string} [input.picture] - The picture URL.
|
||||
* @param {Buffer} [input.pictureBinary] - The picture binary.
|
||||
* @param {string} [input.sub] - The Google sub identifier.
|
||||
* @returns {Promise<void>}
|
||||
* @throws {Error} If the user is not found or the picture binary is invalid.
|
||||
*
|
||||
* @example
|
||||
* await store.google_auth.setProfile(42, { picture: 'url', pictureBinary: buffer, sub: 'sub' });
|
||||
*/
|
||||
return async (
|
||||
googleAuthId,
|
||||
{ picture = null, pictureBinary = null, sub = null } = {},
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { SET_SELECTED_COMPANY } from '../../sql/google_auth.js';
|
||||
|
||||
/**
|
||||
* Sets the currently selected company for a Google auth user.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that sets the selected company.
|
||||
*
|
||||
* @example
|
||||
* const fn = setSelectedCompany(pool);
|
||||
* await fn(42, 1);
|
||||
*/
|
||||
export default function setSelectedCompany(pool) {
|
||||
/**
|
||||
* Sets the currently selected company for a Google auth user.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} companyId - The id of the company to select.
|
||||
* @returns {Promise<void>}
|
||||
*
|
||||
* @example
|
||||
* await store.google_auth.setSelectedCompany(42, 1);
|
||||
*/
|
||||
return async (googleAuthId, companyId) => {
|
||||
await pool
|
||||
.request()
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { SCHEMA_SCRIPTS } from '../schema.js';
|
||||
|
||||
/**
|
||||
* Initializes the database schema by running all schema scripts.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that initializes the schema.
|
||||
*
|
||||
* @example
|
||||
* const fn = initialize(pool);
|
||||
* await fn();
|
||||
*/
|
||||
export default function initialize(pool) {
|
||||
/**
|
||||
* Initializes the database schema by running all schema scripts.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*
|
||||
* @example
|
||||
* await store.initialize();
|
||||
*/
|
||||
return async () => {
|
||||
for (const script of SCHEMA_SCRIPTS) {
|
||||
await pool.request().query(script);
|
||||
|
||||
@@ -1,424 +0,0 @@
|
||||
import {
|
||||
CREATE_LINE,
|
||||
CREATE_MANIFEST,
|
||||
DELETE_LINES_BY_MANIFEST,
|
||||
DELETE_MANIFEST,
|
||||
GET_MANIFEST_HEADER,
|
||||
GET_MANIFEST_LINES,
|
||||
LIST_MANIFESTS,
|
||||
RESOLVE_ARTICLE_ID_BY_SKU,
|
||||
UPDATE_MANIFEST,
|
||||
} from '../../sql/manifestDoc.js';
|
||||
import {
|
||||
GET_CUSTOMER_BY_ID,
|
||||
POS_GROUP_NAME,
|
||||
SUPPLIER_GROUP_NAME,
|
||||
} from '../../sql/entity.js';
|
||||
import request from '../request.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import taxEnsure from '../tax/ensure.js';
|
||||
|
||||
function shapeSummary(type, row) {
|
||||
return {
|
||||
id: row.id,
|
||||
number: row.number,
|
||||
type,
|
||||
entityId: row.entityId ?? null,
|
||||
date: row.date,
|
||||
};
|
||||
}
|
||||
|
||||
function shapeLine(row) {
|
||||
return {
|
||||
id: row.id,
|
||||
articleId: row.articleId ?? null,
|
||||
sku: row.sku ?? null,
|
||||
amount: row.amount,
|
||||
net: row.net,
|
||||
rebate: row.rebate,
|
||||
tax: row.tax,
|
||||
comment: row.comment ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
function parseDate(value) {
|
||||
if (value instanceof Date && !Number.isNaN(value.getTime())) {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === 'string' && value.trim()) {
|
||||
const d = new Date(value);
|
||||
if (!Number.isNaN(d.getTime())) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function normalizeLines(lines) {
|
||||
if (!Array.isArray(lines)) {
|
||||
throw new Error('Invalid lines');
|
||||
}
|
||||
|
||||
return lines.map((entry) => {
|
||||
const sku = entry?.sku?.trim?.() ? entry.sku.trim() : null;
|
||||
if (!sku) {
|
||||
throw new Error('Invalid lines');
|
||||
}
|
||||
|
||||
const amount = entry?.amount;
|
||||
const net = entry?.net;
|
||||
const rebate =
|
||||
entry?.rebate === undefined || entry?.rebate === null ? 0 : entry.rebate;
|
||||
const tax = entry?.tax;
|
||||
const comment =
|
||||
entry?.comment === undefined || entry?.comment === null
|
||||
? null
|
||||
: String(entry.comment);
|
||||
|
||||
if (
|
||||
!Number.isFinite(amount) ||
|
||||
!Number.isFinite(net) ||
|
||||
!Number.isFinite(rebate) ||
|
||||
!Number.isFinite(tax)
|
||||
) {
|
||||
throw new Error('Invalid lines');
|
||||
}
|
||||
|
||||
return { sku, amount, net, rebate, tax, comment };
|
||||
});
|
||||
}
|
||||
|
||||
async function assertCustomerEntity(pool, tx, companyId, entityId) {
|
||||
if (entityId == null) {
|
||||
return null;
|
||||
}
|
||||
if (!Number.isFinite(entityId)) {
|
||||
throw new Error('Entity not found');
|
||||
}
|
||||
|
||||
const result = await request(pool, tx)
|
||||
.input('id', entityId)
|
||||
.input('companyId', companyId)
|
||||
.input('supplierGroupName', SUPPLIER_GROUP_NAME)
|
||||
.input('posGroupName', POS_GROUP_NAME)
|
||||
.query(GET_CUSTOMER_BY_ID);
|
||||
|
||||
if (!result.recordset[0]) {
|
||||
throw new Error('Entity not found');
|
||||
}
|
||||
return entityId;
|
||||
}
|
||||
|
||||
async function resolveArticleId(pool, tx, rootCategoryId, sku) {
|
||||
const result = await request(pool, tx)
|
||||
.input('rootCategoryId', rootCategoryId)
|
||||
.input('sku', sku)
|
||||
.query(RESOLVE_ARTICLE_ID_BY_SKU);
|
||||
|
||||
const articleId = result.recordset[0]?.id;
|
||||
if (!articleId) {
|
||||
throw new Error('Article not found');
|
||||
}
|
||||
return articleId;
|
||||
}
|
||||
|
||||
async function insertLines(
|
||||
pool,
|
||||
tx,
|
||||
{ manifestId, rootCategoryId, lines, ensureTax },
|
||||
) {
|
||||
for (const line of lines) {
|
||||
const articleId = await resolveArticleId(
|
||||
pool,
|
||||
tx,
|
||||
rootCategoryId,
|
||||
line.sku,
|
||||
);
|
||||
const taxId = await ensureTax(line.tax, tx);
|
||||
|
||||
await request(pool, tx)
|
||||
.input('manifestId', manifestId)
|
||||
.input('articleId', articleId)
|
||||
.input('amount', line.amount)
|
||||
.input('net', line.net)
|
||||
.input('comment', line.comment)
|
||||
.input('rebate', line.rebate)
|
||||
.input('taxId', taxId)
|
||||
.query(CREATE_LINE);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDoc(pool, tx, { type, companyId, id }) {
|
||||
const header = await request(pool, tx)
|
||||
.input('id', id)
|
||||
.input('companyId', companyId)
|
||||
.input('type', type)
|
||||
.query(GET_MANIFEST_HEADER);
|
||||
|
||||
const row = header.recordset[0];
|
||||
if (!row) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const lines = await request(pool, tx)
|
||||
.input('manifestId', id)
|
||||
.query(GET_MANIFEST_LINES);
|
||||
|
||||
return {
|
||||
...shapeSummary(type, row),
|
||||
lines: lines.recordset.map(shapeLine),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for quote / order (and similar) stores on manifest / manifestarticle.
|
||||
*
|
||||
* @param {object} pool
|
||||
* @param {{ type: string, label: string }} options
|
||||
* `type` → manifest.type (`quote` / `order`)
|
||||
* `label` → error prefix (`Quote` / `Order` → "Quote not found")
|
||||
*/
|
||||
export default function createManifestDocStore(pool, { type, label }) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const ensureTax = taxEnsure(pool);
|
||||
const notFound = `${label} not found`;
|
||||
const invalid = `Invalid ${label.toLowerCase()}`;
|
||||
|
||||
/**
|
||||
* Lists the quote/order documents of a company.
|
||||
*
|
||||
* @param {number} googleAuthId - The Google auth user id.
|
||||
* @returns {Promise<Array<object>>} The document summaries.
|
||||
*/
|
||||
async function list(googleAuthId) {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
.input('companyId', companyId)
|
||||
.input('type', type)
|
||||
.query(LIST_MANIFESTS);
|
||||
|
||||
return result.recordset.map((row) => shapeSummary(type, row));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single quote/order document by its id within a company.
|
||||
*
|
||||
* @param {number} googleAuthId - The Google auth user id.
|
||||
* @param {number} id - The document id.
|
||||
* @returns {Promise<object|null>} The document with its lines, or null if not found.
|
||||
*/
|
||||
async function getById(googleAuthId, id) {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error(notFound);
|
||||
}
|
||||
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const doc = await loadDoc(pool, null, { type, companyId, id });
|
||||
if (!doc) {
|
||||
throw new Error(notFound);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new quote/order document with its lines.
|
||||
*
|
||||
* @param {number} googleAuthId - The Google auth user id.
|
||||
* @param {object} body - The document payload (`number`, `date`, `entityId`, `lines`).
|
||||
* @returns {Promise<object>} The created document with its lines.
|
||||
*/
|
||||
async function create(googleAuthId, body = {}) {
|
||||
const number = body.number?.trim?.() ? body.number.trim() : null;
|
||||
const date = parseDate(body.date);
|
||||
if (!number || !date) {
|
||||
throw new Error(invalid);
|
||||
}
|
||||
|
||||
const lines = normalizeLines(body.lines ?? []);
|
||||
const { companyId, rootCategoryId } = await access(googleAuthId);
|
||||
|
||||
const tx = pool.transaction();
|
||||
await tx.begin();
|
||||
|
||||
try {
|
||||
const entityId = await assertCustomerEntity(
|
||||
pool,
|
||||
tx,
|
||||
companyId,
|
||||
body.entityId ?? null,
|
||||
);
|
||||
|
||||
const created = await request(pool, tx)
|
||||
.input('companyId', companyId)
|
||||
.input('number', number)
|
||||
.input('type', type)
|
||||
.input('entityId', entityId)
|
||||
.input('date', date)
|
||||
.query(CREATE_MANIFEST);
|
||||
|
||||
const manifestId = created.recordset[0].id;
|
||||
|
||||
await insertLines(pool, tx, {
|
||||
manifestId,
|
||||
rootCategoryId,
|
||||
lines,
|
||||
ensureTax,
|
||||
});
|
||||
|
||||
const doc = await loadDoc(pool, tx, { type, companyId, id: manifestId });
|
||||
await tx.commit();
|
||||
return doc;
|
||||
} catch (err) {
|
||||
try {
|
||||
await tx.rollback();
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a quote/order document's header and/or lines.
|
||||
*
|
||||
* @param {number} googleAuthId - The Google auth user id.
|
||||
* @param {number} id - The document id.
|
||||
* @param {object} patch - The fields to update (`number`, `date`, `entityId`, `lines`).
|
||||
* @returns {Promise<object>} The updated document with its lines.
|
||||
*/
|
||||
async function update(googleAuthId, id, patch = {}) {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error(notFound);
|
||||
}
|
||||
|
||||
const hasNumber = Object.prototype.hasOwnProperty.call(patch, 'number');
|
||||
const hasDate = Object.prototype.hasOwnProperty.call(patch, 'date');
|
||||
const hasEntityId = Object.prototype.hasOwnProperty.call(patch, 'entityId');
|
||||
const hasLines = Object.prototype.hasOwnProperty.call(patch, 'lines');
|
||||
|
||||
if (!hasNumber && !hasDate && !hasEntityId && !hasLines) {
|
||||
throw new Error(invalid);
|
||||
}
|
||||
|
||||
let normalizedLines;
|
||||
if (hasLines) {
|
||||
normalizedLines = normalizeLines(patch.lines);
|
||||
}
|
||||
|
||||
const { companyId, rootCategoryId } = await access(googleAuthId);
|
||||
|
||||
const tx = pool.transaction();
|
||||
await tx.begin();
|
||||
|
||||
try {
|
||||
const existing = await request(pool, tx)
|
||||
.input('id', id)
|
||||
.input('companyId', companyId)
|
||||
.input('type', type)
|
||||
.query(GET_MANIFEST_HEADER);
|
||||
|
||||
const current = existing.recordset[0];
|
||||
if (!current) {
|
||||
throw new Error(notFound);
|
||||
}
|
||||
|
||||
const summary = shapeSummary(type, current);
|
||||
|
||||
let number = summary.number;
|
||||
if (hasNumber) {
|
||||
number = patch.number?.trim?.() ? patch.number.trim() : null;
|
||||
if (!number) {
|
||||
throw new Error(invalid);
|
||||
}
|
||||
}
|
||||
|
||||
let date = summary.date;
|
||||
if (hasDate) {
|
||||
date = parseDate(patch.date);
|
||||
if (!date) {
|
||||
throw new Error(invalid);
|
||||
}
|
||||
}
|
||||
|
||||
let entityId = summary.entityId;
|
||||
if (hasEntityId) {
|
||||
entityId = await assertCustomerEntity(
|
||||
pool,
|
||||
tx,
|
||||
companyId,
|
||||
patch.entityId ?? null,
|
||||
);
|
||||
}
|
||||
|
||||
await request(pool, tx)
|
||||
.input('id', id)
|
||||
.input('companyId', companyId)
|
||||
.input('type', type)
|
||||
.input('number', number)
|
||||
.input('entityId', entityId)
|
||||
.input('date', date)
|
||||
.query(UPDATE_MANIFEST);
|
||||
|
||||
if (hasLines) {
|
||||
await request(pool, tx)
|
||||
.input('manifestId', id)
|
||||
.query(DELETE_LINES_BY_MANIFEST);
|
||||
|
||||
await insertLines(pool, tx, {
|
||||
manifestId: id,
|
||||
rootCategoryId,
|
||||
lines: normalizedLines,
|
||||
ensureTax,
|
||||
});
|
||||
}
|
||||
|
||||
const doc = await loadDoc(pool, tx, { type, companyId, id });
|
||||
await tx.commit();
|
||||
return doc;
|
||||
} catch (err) {
|
||||
try {
|
||||
await tx.rollback();
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a quote/order document by its id within a company.
|
||||
*
|
||||
* @param {number} googleAuthId - The Google auth user id.
|
||||
* @param {number} id - The document id.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function del(googleAuthId, id) {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error(notFound);
|
||||
}
|
||||
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
.input('id', id)
|
||||
.input('companyId', companyId)
|
||||
.input('type', type)
|
||||
.query(DELETE_MANIFEST);
|
||||
|
||||
if (!result.rowsAffected[0]) {
|
||||
throw new Error(notFound);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
list,
|
||||
getById,
|
||||
create,
|
||||
update,
|
||||
delete: del,
|
||||
};
|
||||
}
|
||||
64
src/store/manifestDoc/helpers.js
Normal file
64
src/store/manifestDoc/helpers.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import {
|
||||
GET_MANIFEST_HEADER,
|
||||
GET_MANIFEST_LINES,
|
||||
} from '../../sql/manifestDoc.js';
|
||||
import {
|
||||
GET_CUSTOMER_BY_ID,
|
||||
POS_GROUP_NAME,
|
||||
SUPPLIER_GROUP_NAME,
|
||||
} from '../../sql/entity.js';
|
||||
import request from '../request.js';
|
||||
import { shapeLine } from '../manifestLines.js';
|
||||
|
||||
export function shapeSummary(type, row) {
|
||||
return {
|
||||
id: row.id,
|
||||
number: row.number,
|
||||
type,
|
||||
entityId: row.entityId ?? null,
|
||||
date: row.date,
|
||||
};
|
||||
}
|
||||
|
||||
export async function assertCustomerEntity(pool, tx, companyId, entityId) {
|
||||
if (entityId == null) {
|
||||
return null;
|
||||
}
|
||||
if (!Number.isFinite(entityId)) {
|
||||
throw new Error('Entity not found');
|
||||
}
|
||||
|
||||
const result = await request(pool, tx)
|
||||
.input('id', entityId)
|
||||
.input('companyId', companyId)
|
||||
.input('supplierGroupName', SUPPLIER_GROUP_NAME)
|
||||
.input('posGroupName', POS_GROUP_NAME)
|
||||
.query(GET_CUSTOMER_BY_ID);
|
||||
|
||||
if (!result.recordset[0]) {
|
||||
throw new Error('Entity not found');
|
||||
}
|
||||
return entityId;
|
||||
}
|
||||
|
||||
export async function loadDoc(pool, tx, { type, companyId, id }) {
|
||||
const header = await request(pool, tx)
|
||||
.input('id', id)
|
||||
.input('companyId', companyId)
|
||||
.input('type', type)
|
||||
.query(GET_MANIFEST_HEADER);
|
||||
|
||||
const row = header.recordset[0];
|
||||
if (!row) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const lines = await request(pool, tx)
|
||||
.input('manifestId', id)
|
||||
.query(GET_MANIFEST_LINES);
|
||||
|
||||
return {
|
||||
...shapeSummary(type, row),
|
||||
lines: lines.recordset.map(shapeLine),
|
||||
};
|
||||
}
|
||||
133
src/store/manifestLines.js
Normal file
133
src/store/manifestLines.js
Normal file
@@ -0,0 +1,133 @@
|
||||
import { CREATE_LINE, RESOLVE_ARTICLE_ID_BY_SKU } from '../sql/manifestDoc.js';
|
||||
import request from './request.js';
|
||||
|
||||
/**
|
||||
* Normalizes and validates an array of manifest line inputs.
|
||||
*
|
||||
* @param {Array<object>} lines - The raw line inputs.
|
||||
* @returns {Array<{sku: string, amount: number, net: number, rebate: number, tax: number, comment: string|null}>} The normalized lines.
|
||||
* @throws {Error} If the lines are invalid.
|
||||
*/
|
||||
export function normalizeLines(lines) {
|
||||
if (!Array.isArray(lines)) {
|
||||
throw new Error('Invalid lines');
|
||||
}
|
||||
|
||||
return lines.map((entry) => {
|
||||
const sku = entry?.sku?.trim?.() ? entry.sku.trim() : null;
|
||||
if (!sku) {
|
||||
throw new Error('Invalid lines');
|
||||
}
|
||||
|
||||
const amount = entry?.amount;
|
||||
const net = entry?.net;
|
||||
const rebate =
|
||||
entry?.rebate === undefined || entry?.rebate === null ? 0 : entry.rebate;
|
||||
const tax = entry?.tax;
|
||||
const comment =
|
||||
entry?.comment === undefined || entry?.comment === null
|
||||
? null
|
||||
: String(entry.comment);
|
||||
|
||||
if (
|
||||
!Number.isFinite(amount) ||
|
||||
!Number.isFinite(net) ||
|
||||
!Number.isFinite(rebate) ||
|
||||
!Number.isFinite(tax)
|
||||
) {
|
||||
throw new Error('Invalid lines');
|
||||
}
|
||||
|
||||
return { sku, amount, net, rebate, tax, comment };
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a date value (Date instance or ISO string) into a Date.
|
||||
*
|
||||
* @param {Date|string} value - The date value.
|
||||
* @returns {Date|null} The parsed date, or null if invalid.
|
||||
*/
|
||||
export function parseDate(value) {
|
||||
if (value instanceof Date && !Number.isNaN(value.getTime())) {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === 'string' && value.trim()) {
|
||||
const d = new Date(value);
|
||||
if (!Number.isNaN(d.getTime())) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shapes a manifestarticle row into a line object.
|
||||
*
|
||||
* @param {object} row - The manifestarticle row.
|
||||
* @returns {object} The shaped line.
|
||||
*/
|
||||
export function shapeLine(row) {
|
||||
return {
|
||||
id: row.id,
|
||||
articleId: row.articleId ?? null,
|
||||
sku: row.sku ?? null,
|
||||
amount: row.amount,
|
||||
net: row.net,
|
||||
rebate: row.rebate,
|
||||
tax: row.tax,
|
||||
comment: row.comment ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
async function resolveArticleId(pool, tx, rootCategoryId, sku) {
|
||||
const result = await request(pool, tx)
|
||||
.input('rootCategoryId', rootCategoryId)
|
||||
.input('sku', sku)
|
||||
.query(RESOLVE_ARTICLE_ID_BY_SKU);
|
||||
|
||||
const articleId = result.recordset[0]?.id;
|
||||
if (!articleId) {
|
||||
throw new Error('Article not found');
|
||||
}
|
||||
return articleId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts manifest lines for a document, resolving each SKU to an article id
|
||||
* and ensuring the tax rate.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @param {object} tx - The transaction.
|
||||
* @param {object} params - The insert parameters.
|
||||
* @param {number} params.manifestId - The manifest id.
|
||||
* @param {number} params.rootCategoryId - The company root category id.
|
||||
* @param {Array<object>} params.lines - The normalized lines.
|
||||
* @param {Function} params.ensureTax - The tax ensure function.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function insertLines(
|
||||
pool,
|
||||
tx,
|
||||
{ manifestId, rootCategoryId, lines, ensureTax },
|
||||
) {
|
||||
for (const line of lines) {
|
||||
const articleId = await resolveArticleId(
|
||||
pool,
|
||||
tx,
|
||||
rootCategoryId,
|
||||
line.sku,
|
||||
);
|
||||
const taxId = await ensureTax(line.tax, tx);
|
||||
|
||||
await request(pool, tx)
|
||||
.input('manifestId', manifestId)
|
||||
.input('articleId', articleId)
|
||||
.input('amount', line.amount)
|
||||
.input('net', line.net)
|
||||
.input('comment', line.comment)
|
||||
.input('rebate', line.rebate)
|
||||
.input('taxId', taxId)
|
||||
.query(CREATE_LINE);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,18 @@ import { ENSURE_TYPE } from '../../sql/numbers.js';
|
||||
import request from '../request.js';
|
||||
|
||||
export default function nextNumber(pool) {
|
||||
/**
|
||||
* Allocates and returns the next number for a company and type, consuming it.
|
||||
*
|
||||
* @param {number} companyId - The id of the company.
|
||||
* @param {string} type - The number type (e.g. `'entity'`, `'sku'`).
|
||||
* @param {object} [tx] - Optional transaction to run within.
|
||||
* @returns {Promise<string>} The allocated number.
|
||||
*
|
||||
* @example
|
||||
* const number = await store.numbers.next(1, 'entity');
|
||||
* // => '1'
|
||||
*/
|
||||
return async (companyId, type, tx) => {
|
||||
await request(pool, tx)
|
||||
.input('companyId', companyId)
|
||||
|
||||
@@ -2,5 +2,16 @@ import nextNumber from './nextNumber.js';
|
||||
|
||||
export default function nextSku(pool) {
|
||||
const allocate = nextNumber(pool);
|
||||
/**
|
||||
* Allocates and returns the next SKU for a company, consuming it.
|
||||
*
|
||||
* @param {number} companyId - The id of the company.
|
||||
* @param {object} [tx] - Optional transaction to run within.
|
||||
* @returns {Promise<string>} The allocated SKU.
|
||||
*
|
||||
* @example
|
||||
* const sku = await store.numbers.nextSku(1);
|
||||
* // => '1'
|
||||
*/
|
||||
return (companyId, tx) => allocate(companyId, 'sku', tx);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,18 @@ import { ENSURE_TYPE } from '../../sql/numbers.js';
|
||||
import request from '../request.js';
|
||||
|
||||
export default function peekNumber(pool) {
|
||||
/**
|
||||
* Returns the next number for a company and type without consuming it.
|
||||
*
|
||||
* @param {number} companyId - The id of the company.
|
||||
* @param {string} type - The number type (e.g. `'entity'`, `'sku'`).
|
||||
* @param {object} [tx] - Optional transaction to run within.
|
||||
* @returns {Promise<string>} The next number.
|
||||
*
|
||||
* @example
|
||||
* const number = await store.numbers.peek(1, 'entity');
|
||||
* // => '1'
|
||||
*/
|
||||
return async (companyId, type, tx) => {
|
||||
await request(pool, tx)
|
||||
.input('companyId', companyId)
|
||||
|
||||
82
src/store/order/create.js
Normal file
82
src/store/order/create.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import { CREATE_MANIFEST, ORDER_TYPE } from '../../sql/manifestDoc.js';
|
||||
import request from '../request.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import taxEnsure from '../tax/ensure.js';
|
||||
import { insertLines, normalizeLines, parseDate } from '../manifestLines.js';
|
||||
import { assertCustomerEntity, loadDoc } from '../manifestDoc/helpers.js';
|
||||
|
||||
export default function create(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const ensureTax = taxEnsure(pool);
|
||||
|
||||
/**
|
||||
* Creates a new order for the user's selected company with its lines.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {object} body - The order input.
|
||||
* @param {string} body.number - The order number.
|
||||
* @param {string} body.date - The order date.
|
||||
* @param {number} [body.entityId] - The id of the customer entity.
|
||||
* @param {Array<object>} [body.lines] - The order lines.
|
||||
* @returns {Promise<{id: number, number: string, type: string, entityId: number|null, date: string, lines: Array<object>}>} The created order.
|
||||
* @throws {Error} If the order is invalid.
|
||||
*
|
||||
* @example
|
||||
* const order = await store.order.create(42, { number: 'B-2026-001', date: '2026-09-15', entityId: 12, lines: [...] });
|
||||
* // => { id, number, type, entityId, date, lines: [...] }
|
||||
*/
|
||||
return async (googleAuthId, body = {}) => {
|
||||
const number = body.number?.trim?.() ? body.number.trim() : null;
|
||||
const date = parseDate(body.date);
|
||||
if (!number || !date) {
|
||||
throw new Error('Invalid order');
|
||||
}
|
||||
|
||||
const lines = normalizeLines(body.lines ?? []);
|
||||
const { companyId, rootCategoryId } = await access(googleAuthId);
|
||||
|
||||
const tx = pool.transaction();
|
||||
await tx.begin();
|
||||
|
||||
try {
|
||||
const entityId = await assertCustomerEntity(
|
||||
pool,
|
||||
tx,
|
||||
companyId,
|
||||
body.entityId ?? null,
|
||||
);
|
||||
|
||||
const created = await request(pool, tx)
|
||||
.input('companyId', companyId)
|
||||
.input('number', number)
|
||||
.input('type', ORDER_TYPE)
|
||||
.input('entityId', entityId)
|
||||
.input('date', date)
|
||||
.query(CREATE_MANIFEST);
|
||||
|
||||
const manifestId = created.recordset[0].id;
|
||||
|
||||
await insertLines(pool, tx, {
|
||||
manifestId,
|
||||
rootCategoryId,
|
||||
lines,
|
||||
ensureTax,
|
||||
});
|
||||
|
||||
const doc = await loadDoc(pool, tx, {
|
||||
type: ORDER_TYPE,
|
||||
companyId,
|
||||
id: manifestId,
|
||||
});
|
||||
await tx.commit();
|
||||
return doc;
|
||||
} catch (err) {
|
||||
try {
|
||||
await tx.rollback();
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
}
|
||||
36
src/store/order/delete.js
Normal file
36
src/store/order/delete.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import { DELETE_MANIFEST, ORDER_TYPE } from '../../sql/manifestDoc.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
export default function del(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Deletes an order by its id within the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} id - The id of the order to delete.
|
||||
* @returns {Promise<void>}
|
||||
* @throws {Error} If the order is not found.
|
||||
*
|
||||
* @example
|
||||
* await store.order.delete(42, 5);
|
||||
*/
|
||||
return async (googleAuthId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Order not found');
|
||||
}
|
||||
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
.input('id', id)
|
||||
.input('companyId', companyId)
|
||||
.input('type', ORDER_TYPE)
|
||||
.query(DELETE_MANIFEST);
|
||||
|
||||
if (!result.rowsAffected[0]) {
|
||||
throw new Error('Order not found');
|
||||
}
|
||||
};
|
||||
}
|
||||
33
src/store/order/getById.js
Normal file
33
src/store/order/getById.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { ORDER_TYPE } from '../../sql/manifestDoc.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import { loadDoc } from '../manifestDoc/helpers.js';
|
||||
|
||||
export default function getById(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
/**
|
||||
* Returns a single order by its id within the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} id - The id of the order.
|
||||
* @returns {Promise<{id: number, number: string, type: string, entityId: number|null, date: string, lines: Array<object>}>} The order with its lines.
|
||||
* @throws {Error} If the order is not found.
|
||||
*
|
||||
* @example
|
||||
* const order = await store.order.getById(42, 5);
|
||||
* // => { id, number, type, entityId, date, lines: [...] }
|
||||
*/
|
||||
return async (googleAuthId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Order not found');
|
||||
}
|
||||
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const doc = await loadDoc(pool, null, { type: ORDER_TYPE, companyId, id });
|
||||
if (!doc) {
|
||||
throw new Error('Order not found');
|
||||
}
|
||||
return doc;
|
||||
};
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user