This commit is contained in:
seb
2026-09-24 23:26:25 +02:00
parent 075b1f0ddc
commit f8f61a1212
40 changed files with 1245 additions and 337 deletions

View File

@@ -15,6 +15,7 @@ Adds an image to an article by its SKU, processing it into AVIF variants.
| `googleAuthId` | `number` | The id of the Google auth user. |
| `sku` | `string` | The article SKU. |
| `image` | `Buffer` | The source image buffer. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -41,6 +42,7 @@ Creates a new article under a given parent category, allocating a SKU and a defa
| `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. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -65,6 +67,7 @@ Deletes an article by its SKU, refusing when it still has children.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `sku` | `string` | The article SKU. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -93,6 +96,7 @@ Deletes an image from an article by its SKU and full image hash.
| `googleAuthId` | `number` | The id of the Google auth user. |
| `sku` | `string` | The article SKU. |
| `fullHash` | `string` | The full image hash to delete. |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -120,6 +124,7 @@ Returns the full detail of an article by its SKU, including unit, brand, categor
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `sku` | `string` | The article SKU. |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -148,6 +153,7 @@ Returns the change history of an article by its SKU.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `sku` | `string` | The article SKU. |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -184,6 +190,7 @@ Imports article rows, creating articles by their SKU and returning per-row impor
| `[input.rows[].eans]` | `Array<string>|string` | The EANs to set. |
| `[input.rows[].fatherSku]` | `string` | The father article SKU for a V/K compound link. |
| `[input.rows[].deposit]` | `number|string` | The deposit (Pfand) value; currently ignored. |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -214,6 +221,7 @@ Returns articles for export, optionally scoped to a category, including category
| --- | --- | --- |
| `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. |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -241,6 +249,7 @@ Returns a lightweight search index of all articles for the user's selected compa
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -267,6 +276,7 @@ Reorders the images of an article by its SKU.
| `sku` | `string` | The article SKU. |
| `input` | `object` | The reorder input. |
| `input.fullHashes` | `Array<string>` | The full image hashes in their new order. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -296,6 +306,7 @@ Sets (or clears) the brand of an article by its SKU.
| `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. |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -325,6 +336,7 @@ Replaces the category membership of an article by its SKU.
| `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). |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -354,6 +366,7 @@ Replaces the EAN list of an article by its SKU.
| `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). |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -383,6 +396,7 @@ Links an article to a father article (V/K compound) by their SKUs.
| `googleAuthId` | `number` | The id of the Google auth user. |
| `sku` | `string` | The child article SKU. |
| `fatherSku` | `string|null` | The father article SKU, or `null` to unlink. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -412,6 +426,7 @@ Replaces the price list of an article by its SKU.
| `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). |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -441,6 +456,7 @@ Sets the weight of an article by its SKU.
| `googleAuthId` | `number` | The id of the Google auth user. |
| `sku` | `string` | The article SKU. |
| `weight` | `number` | The weight (must be >= 0). |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -473,6 +489,7 @@ Updates an article's name and/or unit (SI and amount), recording history.
| `[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). |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

View File

@@ -23,6 +23,7 @@ Assigns a supplier to an article, optionally creating a purchase price.
| `[input.netPrice]` | `number|null` | Net purchase price. |
| `[input.taxId]` | `number` | The tax id for the purchase price. |
| `[input.taxRate]` | `number` | The tax rate in percent (used if taxId is not provided). |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -57,6 +58,7 @@ Deletes a supplier assignment and cleans up its associated purchase price.
| `googleAuthId` | `number` | The id of the Google auth user. |
| `sku` | `string` | The article SKU. |
| `id` | `number` | The id of the articlesupplier record. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -84,6 +86,7 @@ Lists the supplier assignments for an article by its SKU.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `sku` | `string` | The article SKU. |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -113,6 +116,7 @@ Lists article-supplier assignments for a given supplier (entityId) and SKU list,
| `googleAuthId` | `number` | The id of the Google auth user. |
| `entityIdOrOptions` | `number|{entityId?: number, supplierId?: number, skus?: string[]|string}` | The supplier entity id or an options object. |
| `[maybeSkus]` | `string[]|string` | The list of SKUs to filter by (or single SKU string). |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -143,6 +147,7 @@ Reorders the supplier assignments of an article by its SKU.
| `sku` | `string` | The article SKU. |
| `input` | `object` | The reorder input. |
| `input.ids` | `Array<number>` | The articlesupplier ids in their new order. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -180,6 +185,7 @@ Updates an existing supplier assignment and its associated purchase price.
| `[patch.netPrice]` | `number|null` | Net purchase price. |
| `[patch.taxId]` | `number` | The tax id for the purchase price. |
| `[patch.taxRate]` | `number` | The tax rate in percent. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

View File

@@ -14,6 +14,7 @@ Deletes a brand for the user's selected company by its id.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `id` | `number` | The id of the brand to delete. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -40,6 +41,7 @@ Lists the brands of the user's selected company.
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -66,6 +68,7 @@ Creates or updates a brand for the user's selected company, keyed by name.
| `input` | `object` | The brand input. |
| `input.name` | `string` | The brand name. |
| `[input.gsprpinfo]` | `string` | Optional GSPRP info. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

View File

@@ -15,6 +15,7 @@ Adds an image to a category, processing it into AVIF full and thumb variants.
| `googleAuthId` | `number` | The id of the Google auth user. |
| `categoryId` | `number` | The id of the category. |
| `image` | `Buffer` | The source image buffer. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -41,6 +42,7 @@ Creates a new category under a given parent category.
| `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. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -66,6 +68,7 @@ Deletes a category, refusing to delete the company's root category.
| `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. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -93,6 +96,7 @@ Deletes the image of a category.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `categoryId` | `number` | The id of the category. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -148,6 +152,7 @@ Returns the articles belonging to a single category.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `categoryId` | `number` | The id of the category. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -171,6 +176,7 @@ Returns the full category tree for the user's selected company together with its
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -197,6 +203,7 @@ Moves a category to a new parent, preventing cycles and cross-company moves.
| `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. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -227,6 +234,7 @@ Reorders the child categories of a given parent.
| `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. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -257,6 +265,7 @@ Renames a category and updates its slug.
| `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. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

View File

@@ -18,6 +18,7 @@ Creates a new company together with its root category, POS customer group, defau
| `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. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -51,6 +52,7 @@ Deletes a company by its id.
| Name | Type | Description |
| --- | --- | --- |
| `companyId` | `number` | The id of the company to delete. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -121,6 +123,7 @@ Lists the companies accessible to the given Google auth user.
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -149,6 +152,7 @@ Updates an existing company's name, language and tax rates.
| `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. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

View File

@@ -17,6 +17,7 @@ Returns a single company setting by key for the user's selected company.
| `[options]` | `object` | Optional options. |
| `[options.frontendOnly]` | `boolean` | Only return the setting if it is frontend-visible. |
| `[options.maskWriteonly]` | `boolean` | Mask writeonly settings as '***'. Defaults to `frontendOnly` (usergui gets '***', backend gets full value). |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -43,6 +44,7 @@ Lists the settings of the user's selected company, optionally only frontend-visi
| `[options]` | `object` | Optional options. |
| `[options.frontendOnly]` | `boolean` | Only return frontend-visible settings. |
| `[options.maskWriteonly]` | `boolean` | Whether to mask writeonly settings with '***'. Defaults to `true`. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -59,7 +61,7 @@ const settings = await store.companysetting.list(42, { frontendOnly: true });
*Source: `src/store/companysetting/set.js`*
Sets (upserts) a company setting by key for the user's selected company. Only a fixed allow-list of keys can be set: `ui.theme`, `keys.tavily`, `keys.openrouter`, `labelprinter.addr`, `labelprinter.type`. Keys in the write-only set (`keys.tavily`, `keys.openrouter`) are stored with `writeonly = 1`; when retrieved by the GUI the value is returned as `"***"`. The backend always receives the real value.
Sets (upserts) a company setting by key for the user's selected company.
**Parameters**
@@ -71,6 +73,7 @@ Sets (upserts) a company setting by key for the user's selected company. Only a
| `input.value` | `string` | The setting value. |
| `[input.frontend]` | `boolean` | Whether the setting is frontend-visible. |
| `[input.writeonly]` | `boolean` | Whether the setting is write-only. Defaults to true for `keys.tavily` and `keys.openrouter`, false otherwise. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

View File

@@ -14,6 +14,7 @@ Adds a company user linking a company to a Google auth account.
| --- | --- | --- |
| `companyId` | `number` | The id of the company. |
| `googleAuthId` | `number` | The id of the Google auth account. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -37,6 +38,7 @@ Deletes a company user link by its id.
| Name | Type | Description |
| --- | --- | --- |
| `id` | `number` | The id of the company user link to delete. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -59,6 +61,7 @@ Returns the company user ids associated with the given email address.
| Name | Type | Description |
| --- | --- | --- |
| `email` | `string` | The email address. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

View File

@@ -15,6 +15,7 @@ Creates a new customer group for the user's selected company.
| `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). |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -43,6 +44,7 @@ Deletes a customer group for the user's selected company by its id.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `id` | `number` | The id of the customer group to delete. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -69,6 +71,7 @@ Lists the customer groups of the user's selected company.
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -94,6 +97,7 @@ Reorders the customer groups of the user's selected company.
| `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. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -124,6 +128,7 @@ Updates the name of a customer group for the user's selected company.
| `id` | `number` | The id of the customer group to update. |
| `input` | `object` | The update input. |
| `input.name` | `string` | The new customer group name. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

View File

@@ -7,147 +7,3 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
*Source: `src/store/delivery/attachments.js`*
Loads attachment metadata (without binary content) for a manifest.
## `delivery.create`
*Source: `src/store/delivery/create.js`*
Creates a new delivery for the user's selected company with its lines. The delivery number is allocated internally via `getnextnumberwithprefixandpadding` (`type` `delivery`); supply `externalNumber` for an external (supplier) reference.
**Parameters**
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `body` | `object` | The delivery input. |
| `body.date` | `string` | The delivery date. |
| `body.supplierId` | `number` | The id of the supplier. |
| `[body.externalNumber]` | `string` | An external (supplier) reference number. |
| `[body.lines]` | `Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>` | The delivery lines. |
**Returns**
`Promise<{id: number, number: string, type: string, entityId: number, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>` — The created delivery.
**Throws**
`Error` — If the delivery is invalid.
**Example**
```js
const delivery = await store.delivery.create(42, { date: '2026-01-01', supplierId: 5, lines: [...] });
// => { id, number, type, entityId, externalNumber, date, lines: [...] }
```
## `delivery.delete`
*Source: `src/store/delivery/delete.js`*
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
await store.delivery.delete(42, 5);
```
## `delivery.getById`
*Source: `src/store/delivery/getById.js`*
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, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>` — The delivery.
**Throws**
`Error` — If the delivery is not found.
**Example**
```js
const delivery = await store.delivery.getById(42, 5);
// => { id, number, type, entityId, externalNumber, date, lines: [...] }
```
## `delivery.list`
*Source: `src/store/delivery/list.js`*
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, externalNumber: string|null, date: string}>>` — The deliveries.
**Example**
```js
const deliveries = await store.delivery.list(42);
// => [{ id, number, type, entityId, externalNumber, date }]
```
## `delivery.update`
*Source: `src/store/delivery/update.js`*
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.externalNumber]` | `string` | The new external (supplier) reference number. |
| `[patch.lines]` | `Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>` | The new delivery lines. |
**Returns**
`Promise<{id: number, number: string, type: string, entityId: number, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>` — The updated delivery.
**Throws**
`Error` — If the delivery is not found.
**Example**
```js
const delivery = await store.delivery.update(42, 5, { number: 'D-2', lines: [...] });
// => { id, number, type, entityId, date, lines: [...] }
```

View File

@@ -6,7 +6,7 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
*Source: `src/store/entity/create.js`*
Creates a new entity (customer) in a customer group, recording history. Optional `number`; if omitted, allocates via `getnextnumberwithprefixandpadding` (`type` `entity`).
Creates a new entity (customer) in a customer group, recording history. Optional `number`; if omitted, allocates via `numbers.nextNumber` (`type` `entity`).
**Parameters**
@@ -18,6 +18,7 @@ Creates a new entity (customer) in a customer group, recording history. Optional
| `input.name` | `string` | The entity name. |
| `[input.firstName]` | `string` | The entity first name. |
| `[input.number]` | `string` | Optional explicit customer number. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -46,6 +47,7 @@ Deletes an entity by its id within the user's selected company.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `id` | `number` | The id of the entity to delete. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -73,6 +75,7 @@ Returns a single entity by its id within the user's selected company.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `id` | `number` | The id of the entity. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -103,6 +106,7 @@ Returns whether a customer number is free within the user's selected company. Pa
| `number` | `string` | The customer number to check. |
| `[options]` | `object` | Optional options. |
| `[options.excludeEntityId]` | `number` | An entity id to exclude from the check. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -135,6 +139,7 @@ Lists the entities (customers) of the user's selected company, optionally filter
| `[options]` | `object` | Optional options. |
| `[options.customergroupId]` | `number` | Filter by customer group id. |
| `[options.role]` | `string` | Restrict to `'customer'` role if set. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -162,6 +167,7 @@ Returns the next customer number that would be allocated for the user's selected
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -190,6 +196,7 @@ Updates name, first name, and/or customer number of an entity, recording history
| `[patch.name]` | `string` | The new entity name. |
| `[patch.firstName]` | `string` | The new first name. |
| `[patch.number]` | `string` | The new customer number. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

View File

@@ -24,6 +24,7 @@ Creates a new address for an entity.
| `[input.city]` | `string` | The city. |
| `[input.county]` | `string` | The county. |
| `[input.sort]` | `number` | Optional sort order. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -53,6 +54,7 @@ Deletes an address of an entity.
| `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. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -80,6 +82,7 @@ Lists the addresses of an entity.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `entityId` | `number` | The id of the entity. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -115,6 +118,7 @@ Updates an address of an entity.
| `[patch.city]` | `string` | The city. |
| `[patch.county]` | `string` | The county. |
| `[patch.sort]` | `number` | The sort order. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

View File

@@ -18,6 +18,7 @@ Creates a new contact for an entity.
| `input.contactType` | `string` | The contact type (e.g. `'email'`). |
| `input.contact` | `string` | The contact value. |
| `[input.sort]` | `number` | Optional sort order. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -47,6 +48,7 @@ Deletes a contact of an entity.
| `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. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -74,6 +76,7 @@ Lists the contacts of an entity.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `entityId` | `number` | The id of the entity. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -100,6 +103,7 @@ Updates a contact of an entity.
| `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). |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

100
API.ledger.md Normal file
View File

@@ -0,0 +1,100 @@
# ledger
This document is auto-generated from the JSDoc comments in `src/store` by running `npm run api-doc`.
## `ledger.add`
*Source: `src/store/ledger/add.js`*
Appends a new append-only entry to the ledger table.
**Parameters**
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `input` | `object` | Ledger entry payload. |
| `input.manifestarticle_id` | `number` | ID of the referenced manifest line (can also pass manifestarticleId). |
| `input.transaction_type` | `string` | Transaction type (can also pass transactionType). |
| `input.amount` | `number` | Movement amount. |
| `input.transaction_text` | `string` | Transaction memo/details (can also pass transactionText). |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
`Promise<{id: number, manifestarticle_id: number, transaction_type: string, amount: number, transaction_text: string, hash_version: number, prev_hash: string, created_at: string}>` — The created ledger row.
**Throws**
`Error` — If user not authorized, company not selected, manifest article not found, or input invalid.
**Example**
```js
const entry = await store.ledger.add(googleAuthId, {
manifestarticle_id: 1,
transaction_type: 'PAYMENT',
amount: 100.5,
transaction_text: 'Invoice paid',
});
```
## `ledger.listByManifestarticleId`
*Source: `src/store/ledger/listByManifestarticleId.js`*
Lists ledger entries for a specific manifest article line, optionally filtered by transaction type.
**Parameters**
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `manifestarticleId` | `number` | The id of the manifest article line. |
| `[optionsOrType]` | `string|object` | Transaction type string or options object { transaction_type?: string, transactionType?: string }. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
`Promise<Array<{id: number, manifestarticle_id: number, sku: string|null, transaction_type: string, amount: number, transaction_text: string, hash_version: number, prev_hash: string, created_at: string}>>`
**Throws**
`Error` — If user not authorized, company not selected, or manifest article not found.
**Example**
```js
const entries = await store.ledger.listByManifestarticleId(googleAuthId, 42, 'PAYMENT');
// => [{ id, manifestarticle_id, sku, transaction_type, amount, transaction_text, hash_version, prev_hash, created_at }]
```
## `ledger.listBySku`
*Source: `src/store/ledger/listBySku.js`*
Lists ledger entries for a given article SKU, optionally filtered by transaction type.
**Parameters**
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `sku` | `string` | The article SKU. |
| `[optionsOrType]` | `string|object` | Transaction type string or options object { transaction_type?: string, transactionType?: string }. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
`Promise<Array<{id: number, manifestarticle_id: number, sku: string|null, transaction_type: string, amount: number, transaction_text: string, hash_version: number, prev_hash: string, created_at: string}>>`
**Throws**
`Error` — If user not authorized, company not selected, or SKU invalid.
**Example**
```js
const entries = await store.ledger.listBySku(googleAuthId, 'SKU-001', 'DELIVERY');
// => [{ id, manifestarticle_id, sku, transaction_type, amount, transaction_text, hash_version, prev_hash, created_at }]
```

View File

@@ -2,51 +2,6 @@
This document is auto-generated from the JSDoc comments in `src/store` by running `npm run api-doc`.
## `manifestattachment.add`
*Source: `src/store/manifestattachment/add.js`*
Attaches a file to an existing manifest.
**Parameters**
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `manifestId` | `number` | The id of the manifest. |
| `input` | `object` | The file data. |
| `input.mimetype` | `string` | The MIME type of the file (e.g. `'application/pdf'`). |
| `input.name` | `string` | The file name. |
| `input.content` | `Buffer` | The file content. |
| `[input.sort]` | `number` | Optional display sort order. |
| `[input.size]` | `number` | Optional file size in bytes (defaults to content length). |
| `[input.created]` | `Date|string` | Optional creation timestamp (defaults to current date). |
| `[input.modified]` | `Date|string` | Optional modified timestamp (defaults to created date). |
| `[input.hash]` | `string` | The file hash (auto-computed SHA-256 if omitted). |
| `[input.thumbavif]` | `Buffer` | Optional AVIF thumbnail buffer. |
| `[input.thumbx]` | `number` | Optional thumbnail width. |
| `[input.thumby]` | `number` | Optional thumbnail height. |
| `[input.meta]` | `object` | Optional string key/value metadata stored on the file row. |
**Returns**
`Promise<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, size: number, created: Date, modified: Date, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null, meta: object|null}>` — The created attachment record.
**Throws**
`Error` — If the manifest is not found or file input is invalid.
**Example**
```js
const attachment = await store.manifestattachment.add(42, 1, {
mimetype: 'application/pdf',
name: 'invoice.pdf',
content: fileBuffer,
});
// => { id, manifestId, fileId, sort, hash, mimetype, name, size, created, modified, thumbavif, thumbx, thumby }
```
## `manifestattachment.getById`
*Source: `src/store/manifestattachment/getById.js`*
@@ -60,6 +15,7 @@ Gets an attachment for a manifest by its attachment ID or file ID, including ful
| `googleAuthId` | `number` | The id of the Google auth user. |
| `manifestId` | `number` | The manifest ID. |
| `id` | `number` | The attachment ID or file ID. |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -96,6 +52,7 @@ Reorders the attachments of a manifest.
| `manifestId` | `number` | The manifest ID. |
| `input` | `object|Array<number>` | The reorder input or array of IDs. |
| `[input.ids]` | `Array<number>` | The attachment ids in their new order. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -125,6 +82,7 @@ Removes a file attachment from a manifest and deletes the underlying file.
| `googleAuthId` | `number` | The id of the Google auth user. |
| `manifestId` | `number` | The manifest ID. |
| `id` | `number` | The attachment ID or file ID to remove. |
| `[tx]` | `object` | Optional transaction. |
**Returns**

204
API.md
View File

@@ -53,45 +53,45 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
## Table of Contents
- [companyuser](API.companyuser.md)
- `add(companyId: number, googleAuthId: number): Promise<number>`
- `delete(id: number): Promise<void>`
- `getByEmail(email: string): Promise<Array<number>>`
- `add(companyId: number, googleAuthId: number, tx?: object): Promise<number>`
- `delete(id: number, tx?: object): Promise<void>`
- `getByEmail(email: string, tx?: object): Promise<Array<number>>`
- [company](API.company.md)
- `create(input: {iso639: string, name: string, taxrate: number, reducedtaxrate: number, googleAuthId: number}): Promise<{id: number, rootCategoryId: number, languageId: number, taxrateId: number, reducedtaxrateId: number, taxfreerateId: number, posCustomerGroupId: number}>`
- `delete(companyId: number): Promise<void>`
- `create(input: {iso639: string, name: string, taxrate: number, reducedtaxrate: number, googleAuthId: number}, tx?: object): Promise<{id: number, rootCategoryId: number, languageId: number, taxrateId: number, reducedtaxrateId: number, taxfreerateId: number, posCustomerGroupId: number}>`
- `delete(companyId: number, tx?: object): Promise<void>`
- `getById(id: number, tx?: object): Promise<object|undefined>`
- `getByName(name: string, tx?: object): Promise<object|undefined>`
- `list(googleAuthId: number): Promise<Array<object>>`
- `update(id: number, input: {iso639: string, name: string, taxrate: number, reducedtaxrate: number}): Promise<{id: number, rootCategoryId: number, languageId: number, taxrateId: number, reducedtaxrateId: number, taxfreerateId: number}>`
- `list(googleAuthId: number, tx?: object): Promise<Array<object>>`
- `update(id: number, input: {iso639: string, name: string, taxrate: number, reducedtaxrate: number}, tx?: object): Promise<{id: number, rootCategoryId: number, languageId: number, taxrateId: number, reducedtaxrateId: number, taxfreerateId: number}>`
- [category](API.category.md)
- `addImage(googleAuthId: number, categoryId: number, image: Buffer): Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>`
- `createUnderParent(input: {googleAuthId: number, parentId: number, name: string}): Promise<{id: number, parentId: number, sort: number, name: string, slug: string}>`
- `delete(input: {googleAuthId: number, id: number}): Promise<void>`
- `deleteImage(googleAuthId: number, categoryId: number): Promise<{id: number, thumbW: null, thumbH: null, thumbHash: null}>`
- `addImage(googleAuthId: number, categoryId: number, image: Buffer, tx?: object): Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>`
- `createUnderParent(input: {googleAuthId: number, parentId: number, name: string}, tx?: object): Promise<{id: number, parentId: number, sort: number, name: string, slug: string}>`
- `delete(input: {googleAuthId: number, id: number}, tx?: object): Promise<void>`
- `deleteImage(googleAuthId: number, categoryId: number, tx?: object): Promise<{id: number, thumbW: null, thumbH: null, thumbHash: null}>`
- `ensureByPath(input: {googleAuthId: number, parentId: number, path: string}, tx?: object): Promise<number>`
- `getCategoryArticles(googleAuthId: number, categoryId: number): Promise<Array<object>>`
- `getTreeForCompany(googleAuthId: number): Promise<{languageId: number, tree: Array<object>, articles: Array<object>}>`
- `move(input: {googleAuthId: number, id: number, parentId: number}): Promise<{id: number, parentId: number, sort: number}>`
- `order(input: {googleAuthId: number, parentId: number, categoryIds: Array<number>}): Promise<{categoryIds: Array<number>}>`
- `rename(input: {googleAuthId: number, id: number, name: string}): Promise<{id: number, name: string, slug: string}>`
- `getCategoryArticles(googleAuthId: number, categoryId: number, tx?: object): Promise<Array<object>>`
- `getTreeForCompany(googleAuthId: number, tx?: object): Promise<{languageId: number, tree: Array<object>, articles: Array<object>}>`
- `move(input: {googleAuthId: number, id: number, parentId: number}, tx?: object): Promise<{id: number, parentId: number, sort: number}>`
- `order(input: {googleAuthId: number, parentId: number, categoryIds: Array<number>}, tx?: object): Promise<{categoryIds: Array<number>}>`
- `rename(input: {googleAuthId: number, id: number, name: string}, tx?: object): Promise<{id: number, name: string, slug: string}>`
- [article](API.article.md)
- `addImage(googleAuthId: number, sku: string, image: Buffer): Promise<{picId: number, sort: number, full: {w: number, h: number, size: number}, thumb: {w: number, h: number}, fullHash: string, thumbHash: string}>`
- `createUnderParent(input: {googleAuthId: number, parentId: number, name: string}): Promise<{id: number, parentId: number, name: string, sku: string}>`
- `delete(googleAuthId: number, sku: string): Promise<void>`
- `deleteImage(googleAuthId: number, sku: string, fullHash: string): Promise<void>`
- `getBySku(googleAuthId: number, sku: string): Promise<{article: {id: number, sku: string, name: string, slug: string, unitamount: number|null, weight: number|null, unit: {si: string|null, basesi: string|null, basesifactor: number|null, symbol: string|null, name: string|null}, brand: {name: string, thumbW: number, thumbH: number, thumbHash: string}|null, categoryIds: number[], photos: Array<{sort: number, fullHash: string, fullW: number, fullH: number, fullSize: number, thumbW: number, thumbH: number, thumbHash: string}>, eans: string[], prices: Array<{minmultiplier: number, net: number, taxRate: number, customergroupId: number}>, suppliers: Array<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null}>, optionsCount: number, articleListCount: number, childCount: number, fatherArticleId: number|null, fatherSku: string|null}}>`
- `getHistoryBySku(googleAuthId: number, sku: string): Promise<Array<{id: number, companyuserId: number|null, date: string, text: string}>>`
- `importRows(input: {googleAuthId: number, rows: Array<object>}): Promise<{imported: Array<object>, skipped: Array<object>, errors: Array<object>}>`
- `listExport(googleAuthId: number, categoryId?: number|null): Promise<Array<object>>`
- `listSearchIndex(googleAuthId: number): Promise<Array<{id: number, sku: string, name: string, eans: Array<string>, categoryIds: Array<number>}>>`
- `orderImages(googleAuthId: number, sku: string, input: {fullHashes: Array<string>}): Promise<{fullHashes: Array<string>}>`
- `setBrandIdBySku(googleAuthId: number, sku: string, brandId: number|null): Promise<object>`
- `setCategoryIdsBySku(googleAuthId: number, sku: string, categoryIds: Array<number>): Promise<object>`
- `setEansBySku(googleAuthId: number, sku: string, eans: Array<string>): Promise<object>`
- `setFatherBySku(googleAuthId: number, sku: string, fatherSku: string|null): Promise<object>`
- `setPricesBySku(googleAuthId: number, sku: string, prices: Array<{price: number, tax: number, minmultiplier?: number}>): Promise<object>`
- `setWeightBySku(googleAuthId: number, sku: string, weight: number): Promise<object>`
- `updateBySku(googleAuthId: number, sku: string, patch: {name?: string, unitSi?: string, unitamount?: number}): Promise<object>`
- `addImage(googleAuthId: number, sku: string, image: Buffer, tx?: object): Promise<{picId: number, sort: number, full: {w: number, h: number, size: number}, thumb: {w: number, h: number}, fullHash: string, thumbHash: string}>`
- `createUnderParent(input: {googleAuthId: number, parentId: number, name: string}, tx?: object): Promise<{id: number, parentId: number, name: string, sku: string}>`
- `delete(googleAuthId: number, sku: string, tx?: object): Promise<void>`
- `deleteImage(googleAuthId: number, sku: string, fullHash: string, tx?: object): Promise<void>`
- `getBySku(googleAuthId: number, sku: string, tx?: object): Promise<{article: {id: number, sku: string, name: string, slug: string, unitamount: number|null, weight: number|null, unit: {si: string|null, basesi: string|null, basesifactor: number|null, symbol: string|null, name: string|null}, brand: {name: string, thumbW: number, thumbH: number, thumbHash: string}|null, categoryIds: number[], photos: Array<{sort: number, fullHash: string, fullW: number, fullH: number, fullSize: number, thumbW: number, thumbH: number, thumbHash: string}>, eans: string[], prices: Array<{minmultiplier: number, net: number, taxRate: number, customergroupId: number}>, suppliers: Array<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null}>, optionsCount: number, articleListCount: number, childCount: number, fatherArticleId: number|null, fatherSku: string|null}}>`
- `getHistoryBySku(googleAuthId: number, sku: string, tx?: object): Promise<Array<{id: number, companyuserId: number|null, date: string, text: string}>>`
- `importRows(input: {googleAuthId: number, rows: Array<object>}, tx?: object): Promise<{imported: Array<object>, skipped: Array<object>, errors: Array<object>}>`
- `listExport(googleAuthId: number, categoryId?: number|null, tx?: object): Promise<Array<object>>`
- `listSearchIndex(googleAuthId: number, tx?: object): Promise<Array<{id: number, sku: string, name: string, eans: Array<string>, categoryIds: Array<number>}>>`
- `orderImages(googleAuthId: number, sku: string, input: {fullHashes: Array<string>}, tx?: object): Promise<{fullHashes: Array<string>}>`
- `setBrandIdBySku(googleAuthId: number, sku: string, brandId: number|null, tx?: object): Promise<object>`
- `setCategoryIdsBySku(googleAuthId: number, sku: string, categoryIds: Array<number>, tx?: object): Promise<object>`
- `setEansBySku(googleAuthId: number, sku: string, eans: Array<string>, tx?: object): Promise<object>`
- `setFatherBySku(googleAuthId: number, sku: string, fatherSku: string|null, tx?: object): Promise<object>`
- `setPricesBySku(googleAuthId: number, sku: string, prices: Array<{price: number, tax: number, minmultiplier?: number}>, tx?: object): Promise<object>`
- `setWeightBySku(googleAuthId: number, sku: string, weight: number, tx?: object): Promise<object>`
- `updateBySku(googleAuthId: number, sku: string, patch: {name?: string, unitSi?: string, unitamount?: number}, tx?: object): Promise<object>`
- [lang](API.lang.md)
- `create(iso639: string, tx?: object): Promise<number>`
- `delete(id: number): Promise<void>`
@@ -104,86 +104,84 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
- `getByRate(rate: number, tx?: object): Promise<{id: number, rate: number}>`
- `list(): Promise<Array<{id: number, rate: number}>>`
- [unit](API.unit.md)
- `delete(googleAuthId: number, si: string): Promise<void>`
- `list(googleAuthId: number): Promise<Array<{id: number, si: string, basesi: string, basesifactor: number, symbol: string, name: string}>>`
- `upsert(googleAuthId: number, input: {si: string, basesi: string, basesifactor: number, symbol: string, name: string}): Promise<{id: number, si: string, basesi: string, basesifactor: number, symbol: string, name: string}>`
- `delete(googleAuthId: number, si: string, tx?: object): Promise<void>`
- `list(googleAuthId: number, tx?: object): Promise<Array<{id: number, si: string, basesi: string, basesifactor: number, symbol: string, name: string}>>`
- `upsert(googleAuthId: number, input: {si: string, basesi: string, basesifactor: number, symbol: string, name: string}, tx?: object): Promise<{id: number, si: string, basesi: string, basesifactor: number, symbol: string, name: string}>`
- [companysetting](API.companysetting.md)
- `get(googleAuthId: number, key: string, options?: {frontendOnly?: boolean, maskWriteonly?: boolean}): Promise<{key: string, value: string, frontend: boolean, writeonly: boolean}|undefined>`
- `list(googleAuthId: number, options?: {frontendOnly?: boolean, maskWriteonly?: boolean}): Promise<Array<{key: string, value: string, frontend: boolean, writeonly: boolean}>>`
- `set(googleAuthId: number, input: {key: string, value: string, frontend?: boolean, writeonly?: boolean}): Promise<{key: string, value: string, frontend: boolean, writeonly: boolean}>`
- `get(googleAuthId: number, key: string, options?: {frontendOnly?: boolean, maskWriteonly?: boolean}, tx?: object): Promise<{key: string, value: string, frontend: boolean, writeonly: boolean}|undefined>`
- `list(googleAuthId: number, options?: {frontendOnly?: boolean, maskWriteonly?: boolean}, tx?: object): Promise<Array<{key: string, value: string, frontend: boolean, writeonly: boolean}>>`
- `set(googleAuthId: number, input: {key: string, value: string, frontend?: boolean, writeonly?: boolean}, tx?: object): Promise<{key: string, value: string, frontend: boolean, writeonly: boolean}>`
- [brand](API.brand.md)
- `delete(googleAuthId: number, id: number): Promise<void>`
- `list(googleAuthId: number): Promise<Array<{id: number, name: string, gsprpinfo: string|null}>>`
- `upsert(googleAuthId: number, input: {name: string, gsprpinfo?: string}): Promise<{id: number, name: string, gsprpinfo: string|null}>`
- `delete(googleAuthId: number, id: number, tx?: object): Promise<void>`
- `list(googleAuthId: number, tx?: object): Promise<Array<{id: number, name: string, gsprpinfo: string|null}>>`
- `upsert(googleAuthId: number, input: {name: string, gsprpinfo?: string}, tx?: object): Promise<{id: number, name: string, gsprpinfo: string|null}>`
- [customergroup](API.customergroup.md)
- `create(googleAuthId: number, input: {name: string}): Promise<{id: number, name: string, sort: number}>`
- `delete(googleAuthId: number, id: number): Promise<void>`
- `list(googleAuthId: number): Promise<Array<{id: number, name: string, sort: number}>>`
- `order(googleAuthId: number, input: {customergroupIds: Array<number>}): Promise<{customergroupIds: Array<number>}>`
- `update(googleAuthId: number, id: number, input: {name: string}): Promise<{id: number, name: string, sort: number}>`
- `create(googleAuthId: number, input: {name: string}, tx?: object): Promise<{id: number, name: string, sort: number}>`
- `delete(googleAuthId: number, id: number, tx?: object): Promise<void>`
- `list(googleAuthId: number, tx?: object): Promise<Array<{id: number, name: string, sort: number}>>`
- `order(googleAuthId: number, input: {customergroupIds: Array<number>}, tx?: object): Promise<{customergroupIds: Array<number>}>`
- `update(googleAuthId: number, id: number, input: {name: string}, tx?: object): Promise<{id: number, name: string, sort: number}>`
- [entity](API.entity.md)
- `create(googleAuthId: number, input: {customergroupId: number, name: string, firstName?: string, number?: string}): Promise<{id: number, customergroupId: number, firstName: string|null, name: string, number: string, createdAt: string, updatedAt: string}>`
- `delete(googleAuthId: number, id: number): Promise<void>`
- `getById(googleAuthId: number, id: number): Promise<{id: number, customergroupId: number, firstName: string|null, name: string, number: string, createdAt: string, updatedAt: string}>`
- `isNumberFree(googleAuthId: number, number: string, options?: {excludeEntityId?: number}): Promise<boolean>`
- `list(googleAuthId: number, options?: {customergroupId?: number, role?: string}): Promise<Array<{id: number, customergroupId: number, firstName: string|null, name: string, number: string, createdAt: string, updatedAt: string}>>`
- `peekNextNumber(googleAuthId: number): Promise<string>`
- `update(googleAuthId: number, id: number, patch: {name?: string, firstName?: string, number?: string}): Promise<{id: number, customergroupId: number, firstName: string|null, name: string, number: string, createdAt: string, updatedAt: string}>`
- `create(googleAuthId: number, input: {customergroupId: number, name: string, firstName?: string, number?: string}, tx?: object): Promise<{id: number, customergroupId: number, firstName: string|null, name: string, number: string, createdAt: string, updatedAt: string}>`
- `delete(googleAuthId: number, id: number, tx?: object): Promise<void>`
- `getById(googleAuthId: number, id: number, tx?: object): Promise<{id: number, customergroupId: number, firstName: string|null, name: string, number: string, createdAt: string, updatedAt: string}>`
- `isNumberFree(googleAuthId: number, number: string, options?: {excludeEntityId?: number}, tx?: object): Promise<boolean>`
- `list(googleAuthId: number, options?: {customergroupId?: number, role?: string}, tx?: object): Promise<Array<{id: number, customergroupId: number, firstName: string|null, name: string, number: string, createdAt: string, updatedAt: string}>>`
- `peekNextNumber(googleAuthId: number, tx?: object): Promise<string>`
- `update(googleAuthId: number, id: number, patch: {name?: string, firstName?: string, number?: string}, tx?: object): Promise<{id: number, customergroupId: number, firstName: string|null, name: string, number: string, createdAt: string, updatedAt: string}>`
- [entityaddress](API.entityaddress.md)
- `create(googleAuthId: number, entityId: number, input: {addressType: string, firstName?: string, name?: string, street?: string, streetNumber?: string, zip?: string, city?: string, county?: string, sort?: number}): Promise<{id: number, entityId: number, firstName: string|null, name: string|null, street: string|null, streetNumber: string|null, zip: string|null, city: string|null, county: string|null, addressType: string, sort: number}>`
- `delete(googleAuthId: number, entityId: number, id: number): Promise<void>`
- `list(googleAuthId: number, entityId: number): Promise<Array<{id: number, entityId: number, firstName: string|null, name: string|null, street: string|null, streetNumber: string|null, zip: string|null, city: string|null, county: string|null, addressType: string, sort: number}>>`
- `update(googleAuthId: number, entityId: number, id: number, patch: {addressType?: string, firstName?: string, name?: string, street?: string, streetNumber?: string, zip?: string, city?: string, county?: string, sort?: number}): Promise<{id: number, entityId: number, firstName: string|null, name: string|null, street: string|null, streetNumber: string|null, zip: string|null, city: string|null, county: string|null, addressType: string, sort: number}>`
- `create(googleAuthId: number, entityId: number, input: {addressType: string, firstName?: string, name?: string, street?: string, streetNumber?: string, zip?: string, city?: string, county?: string, sort?: number}, tx?: object): Promise<{id: number, entityId: number, firstName: string|null, name: string|null, street: string|null, streetNumber: string|null, zip: string|null, city: string|null, county: string|null, addressType: string, sort: number}>`
- `delete(googleAuthId: number, entityId: number, id: number, tx?: object): Promise<void>`
- `list(googleAuthId: number, entityId: number, tx?: object): Promise<Array<{id: number, entityId: number, firstName: string|null, name: string|null, street: string|null, streetNumber: string|null, zip: string|null, city: string|null, county: string|null, addressType: string, sort: number}>>`
- `update(googleAuthId: number, entityId: number, id: number, patch: {addressType?: string, firstName?: string, name?: string, street?: string, streetNumber?: string, zip?: string, city?: string, county?: string, sort?: number}, tx?: object): Promise<{id: number, entityId: number, firstName: string|null, name: string|null, street: string|null, streetNumber: string|null, zip: string|null, city: string|null, county: string|null, addressType: string, sort: number}>`
- [entitycontact](API.entitycontact.md)
- `create(googleAuthId: number, entityId: number, input: {contactType: string, contact: string, sort?: number}): Promise<{id: number, entityId: number, contactType: string, contact: string, sort: number}>`
- `delete(googleAuthId: number, entityId: number, id: number): Promise<void>`
- `list(googleAuthId: number, entityId: number): Promise<Array<{id: number, entityId: number, contactType: string, contact: string, sort: number}>>`
- `update(googleAuthId: number, entityId: number, id: number, patch: object): Promise<{id: number, entityId: number, contactType: string, contact: string, sort: number}>`
- `create(googleAuthId: number, entityId: number, input: {contactType: string, contact: string, sort?: number}, tx?: object): Promise<{id: number, entityId: number, contactType: string, contact: string, sort: number}>`
- `delete(googleAuthId: number, entityId: number, id: number, tx?: object): Promise<void>`
- `list(googleAuthId: number, entityId: number, tx?: object): Promise<Array<{id: number, entityId: number, contactType: string, contact: string, sort: number}>>`
- `update(googleAuthId: number, entityId: number, id: number, patch: object, tx?: object): Promise<{id: number, entityId: number, contactType: string, contact: string, sort: number}>`
- [supplier](API.supplier.md)
- `create(googleAuthId: number, input: {name: string}): Promise<{id: number, name: string, createdAt: string, updatedAt: string}>`
- `delete(googleAuthId: number, id: number): Promise<void>`
- `list(googleAuthId: number): Promise<Array<{id: number, name: string, createdAt: string, updatedAt: string}>>`
- `update(googleAuthId: number, id: number, input: {name: string}): Promise<{id: number, name: string, createdAt: string, updatedAt: string}>`
- `create(googleAuthId: number, input: {name: string}, tx?: object): Promise<{id: number, name: string, createdAt: string, updatedAt: string}>`
- `delete(googleAuthId: number, id: number, tx?: object): Promise<void>`
- `list(googleAuthId: number, tx?: object): Promise<Array<{id: number, name: string, createdAt: string, updatedAt: string}>>`
- `update(googleAuthId: number, id: number, input: {name: string}, tx?: object): Promise<{id: number, name: string, createdAt: string, updatedAt: string}>`
- [articlesupplier](API.articlesupplier.md)
- `create(googleAuthId: number, sku: string, input: {entityId: number, sku?: string, pu?: number, stock?: number|null, sort?: number, netPrice?: number|null, taxId?: number, taxRate?: number}): Promise<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null}>`
- `delete(googleAuthId: number, sku: string, id: number): Promise<void>`
- `listByArticleSku(googleAuthId: number, sku: string): Promise<Array<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null}>>`
- `listByEntityAndSkus(googleAuthId: number, entityIdOrOptions: number|{entityId?: number, supplierId?: number, skus?: string[]|string}, maybeSkus?: string[]|string): Promise<Array<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null, article: {id: number, sku: string, name: string|null, slug: string|null, photos: Array<{sort: number, thumbhash: string, hash: string, thumbw: number, thumbh: number}>, articlepic: Array<{sort: number, thumbhash: string, hash: string, thumbw: number, thumbh: number}>, eans: string[], articleean: string[]}}>>`
- `order(googleAuthId: number, sku: string, input: {ids: Array<number>}): Promise<{ids: Array<number>}>`
- `update(googleAuthId: number, sku: string, id: number, patch: {sku?: string|null, pu?: number, stock?: number|null, sort?: number, netPrice?: number|null, taxId?: number, taxRate?: number}): Promise<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null}>`
- `create(googleAuthId: number, sku: string, input: {entityId: number, sku?: string, pu?: number, stock?: number|null, sort?: number, netPrice?: number|null, taxId?: number, taxRate?: number}, tx?: object): Promise<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null}>`
- `delete(googleAuthId: number, sku: string, id: number, tx?: object): Promise<void>`
- `listByArticleSku(googleAuthId: number, sku: string, tx?: object): Promise<Array<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null}>>`
- `listByEntityAndSkus(googleAuthId: number, entityIdOrOptions: number|{entityId?: number, supplierId?: number, skus?: string[]|string}, maybeSkus?: string[]|string, tx?: object): Promise<Array<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null, article: {id: number, sku: string, name: string|null, slug: string|null, photos: Array<{sort: number, thumbhash: string, hash: string, thumbw: number, thumbh: number}>, articlepic: Array<{sort: number, thumbhash: string, hash: string, thumbw: number, thumbh: number}>, eans: string[], articleean: string[]}}>>`
- `order(googleAuthId: number, sku: string, input: {ids: Array<number>}, tx?: object): Promise<{ids: Array<number>}>`
- `update(googleAuthId: number, sku: string, id: number, patch: {sku?: string|null, pu?: number, stock?: number|null, sort?: number, netPrice?: number|null, taxId?: number, taxRate?: number}, tx?: object): Promise<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null}>`
- [delivery](API.delivery.md)
- `attachments(): void`
- `create(googleAuthId: number, body: {date: string, supplierId: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}): Promise<{id: number, number: string, type: string, entityId: number, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- `delete(googleAuthId: number, id: number): Promise<void>`
- `getById(googleAuthId: number, id: number): Promise<{id: number, number: string, type: string, entityId: number, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- `list(googleAuthId: number): Promise<Array<{id: number, number: string, type: string, entityId: number, externalNumber: string|null, date: string}>>`
- `update(googleAuthId: number, id: number, patch: {number?: string, date?: string, supplierId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}): Promise<{id: number, number: string, type: string, entityId: number, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- [quote](API.quote.md)
- `create(googleAuthId: number, body: {number: string, date: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- `delete(googleAuthId: number, id: number): Promise<void>`
- `getById(googleAuthId: number, id: number): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- `list(googleAuthId: number): Promise<Array<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string}>>`
- `update(googleAuthId: number, id: number, patch: {number?: string, date?: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- `create(googleAuthId: number, body: {number: string, date: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- `delete(googleAuthId: number, id: number, tx?: object): Promise<void>`
- `getById(googleAuthId: number, id: number, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- `list(googleAuthId: number, tx?: object): Promise<Array<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string}>>`
- `update(googleAuthId: number, id: number, patch: {number?: string, date?: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- [order](API.order.md)
- `create(googleAuthId: number, body: {number: string, date: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- `delete(googleAuthId: number, id: number): Promise<void>`
- `getById(googleAuthId: number, id: number): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- `list(googleAuthId: number): Promise<Array<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string}>>`
- `update(googleAuthId: number, id: number, patch: {number?: string, date?: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- `create(googleAuthId: number, body: {number: string, date: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- `delete(googleAuthId: number, id: number, tx?: object): Promise<void>`
- `getById(googleAuthId: number, id: number, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- `list(googleAuthId: number, tx?: object): Promise<Array<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string}>>`
- `update(googleAuthId: number, id: number, patch: {number?: string, date?: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- [manifestattachment](API.manifestattachment.md)
- `add(googleAuthId: number, manifestId: number, input: {mimetype: string, name: string, content: Buffer, sort?: number, size?: number, created?: Date|string, modified?: Date|string, hash?: string, thumbavif?: Buffer, thumbx?: number, thumby?: number, meta?: object}): Promise<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, size: number, created: Date, modified: Date, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null, meta: object|null}>`
- `getById(googleAuthId: number, manifestId: number, id: number): Promise<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, size: number, created: Date, modified: Date, content: Buffer, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null, meta: object|null}>`
- `getById(googleAuthId: number, manifestId: number, id: number, tx?: object): Promise<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, size: number, created: Date, modified: Date, content: Buffer, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null, meta: object|null}>`
- `listByManifestId(): void`
- `order(googleAuthId: number, manifestId: number, input: {ids?: Array<number>}): Promise<{ids: Array<number>}>`
- `remove(googleAuthId: number, manifestId: number, id: number): Promise<void>`
- `order(googleAuthId: number, manifestId: number, input: {ids?: Array<number>}, tx?: object): Promise<{ids: Array<number>}>`
- `remove(googleAuthId: number, manifestId: number, id: number, tx?: object): Promise<void>`
- [ledger](API.ledger.md)
- `add(googleAuthId: number, input: {manifestarticle_id: number, transaction_type: string, amount: number, transaction_text: string}, tx?: object): Promise<{id: number, manifestarticle_id: number, transaction_type: string, amount: number, transaction_text: string, hash_version: number, prev_hash: string, created_at: string}>`
- `listByManifestarticleId(googleAuthId: number, manifestarticleId: number, optionsOrType?: string|object, tx?: object): Promise<Array<{id: number, manifestarticle_id: number, sku: string|null, transaction_type: string, amount: number, transaction_text: string, hash_version: number, prev_hash: string, created_at: string}>>`
- `listBySku(googleAuthId: number, sku: string, optionsOrType?: string|object, tx?: object): Promise<Array<{id: number, manifestarticle_id: number, sku: string|null, transaction_type: string, amount: number, transaction_text: string, hash_version: number, prev_hash: string, created_at: string}>>`
- [posterminal](API.posterminal.md)
- `delete(googleAuthId: number, id: number): Promise<void>`
- `list(googleAuthId: number): Promise<Array<{id: number, name: string, token: string, pairingpin: string}>>`
- `setOff(googleAuthId: number, posterminalId: number, categoryId: number): Promise<{id: number, categoryIds: Array<number>}>`
- `setOffAll(googleAuthId: number, posterminalId: number): Promise<{id: number, categoryIds: Array<number>}>`
- `setOn(googleAuthId: number, posterminalId: number, categoryId: number): Promise<{id: number, categoryIds: Array<number>}>`
- `setOnAll(googleAuthId: number, posterminalId: number): Promise<{id: number, categoryIds: Array<number>}>`
- `upsert(googleAuthId: number, input: {name: string, pairingpin: string, token?: string, id?: number}): Promise<{id: number, name: string, token: string, pairingpin: string}>`
- `delete(googleAuthId: number, id: number, tx?: object): Promise<void>`
- `list(googleAuthId: number, tx?: object): Promise<Array<{id: number, name: string, token: string, pairingpin: string}>>`
- `setOff(googleAuthId: number, posterminalId: number, categoryId: number, tx?: object): Promise<{id: number, categoryIds: Array<number>}>`
- `setOffAll(googleAuthId: number, posterminalId: number, tx?: object): Promise<{id: number, categoryIds: Array<number>}>`
- `setOn(googleAuthId: number, posterminalId: number, categoryId: number, tx?: object): Promise<{id: number, categoryIds: Array<number>}>`
- `setOnAll(googleAuthId: number, posterminalId: number, tx?: object): Promise<{id: number, categoryIds: Array<number>}>`
- `upsert(googleAuthId: number, input: {name: string, pairingpin: string, token?: string, id?: number}, tx?: object): Promise<{id: number, name: string, token: string, pairingpin: string}>`
- [google_auth](API.google_auth.md)
- `add(email: string): Promise<number>`
- `getByEmail(email: string): Promise<object|undefined>`
@@ -193,8 +191,10 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
- `setProfile(googleAuthId: number, input: {picture?: string, pictureBinary?: Buffer, sub?: string}): Promise<void>`
- `setSelectedCompany(googleAuthId: number, companyId: number): Promise<void>`
- [pic](API.pic.md)
- `getImageByHash(googleAuthId: number, fullHash: string): Promise<{buffer: Buffer, w: number, h: number, size: number, contentType: string}>`
- `getThumbByHash(googleAuthId: number, fullHash: string): Promise<{buffer: Buffer, w: number, h: number, contentType: string}>`
- `getImageByHash(googleAuthId: number, fullHash: string, tx?: object): Promise<{buffer: Buffer, w: number, h: number, size: number, contentType: string}>`
- `getThumbByHash(googleAuthId: number, fullHash: string, tx?: object): Promise<{buffer: Buffer, w: number, h: number, contentType: string}>`
- [numbers](API.numbers.md)
- `list(googleAuthId: number): Promise<Array<{id: number, type: string, prefix: string, padding: number, next: number}>>`
- `update(googleAuthId: number, id: number, input: {prefix?: string, padding?: number, next?: number}): Promise<{id: number, type: string, prefix: string, padding: number, next: number}>`
- `list(googleAuthId: number, tx?: object): Promise<Array<{id: number, type: string, prefix: string, padding: number, next: number}>>`
- `update(googleAuthId: number, id: number, input: {prefix?: string, padding?: number, next?: number}, tx?: object): Promise<{id: number, type: string, prefix: string, padding: number, next: number}>`
- [runner.js](API.runner.js.md)
- `runner(provider: import('../providers/provider.js').DatabaseProvider, tx?: object): ((sql: string, params?: object) => Promise<import('../providers/provider.js').QueryResult>) & { query: Function, dialect?: string }`

View File

@@ -13,6 +13,7 @@ Lists the number sequences of the user's selected company.
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -41,6 +42,7 @@ Updates the sequence settings of a number for the user's selected company. Only
| `[input.prefix]` | `string` | The new prefix. |
| `[input.padding]` | `number` | The new padding length. |
| `[input.next]` | `number` | The new next value. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

View File

@@ -19,6 +19,7 @@ Creates a new order for the user's selected company with its lines.
| `[body.entityId]` | `number` | The id of the customer entity. |
| `[body.externalNumber]` | `string` | An external (customer) reference number. |
| `[body.lines]` | `Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>` | The order lines. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -47,6 +48,7 @@ Deletes an order by its id within the user's selected company.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `id` | `number` | The id of the order to delete. |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -74,6 +76,7 @@ Returns a single order by its id within the user's selected company.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `id` | `number` | The id of the order. |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -101,6 +104,7 @@ Lists the orders of the user's selected company.
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -131,6 +135,7 @@ Updates an order's header and/or lines.
| `[patch.entityId]` | `number` | The new customer entity id. |
| `[patch.externalNumber]` | `string` | The new external (customer) reference number. |
| `[patch.lines]` | `Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>` | The new order lines. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

View File

@@ -14,6 +14,7 @@ Returns the full-size image (AVIF) for a given hash.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `fullHash` | `string` | The full image hash. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -42,6 +43,7 @@ Returns the thumbnail image (AVIF) for a given hash.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `fullHash` | `string` | The full image hash. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

View File

@@ -14,6 +14,7 @@ Deletes a posterminal by its id within the user's selected company.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `id` | `number` | The id of the posterminal to delete. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -40,6 +41,7 @@ Lists the posterminals of the user's selected company.
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -65,6 +67,7 @@ Removes a category assignment from a posterminal (turns it off).
| `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. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -93,6 +96,7 @@ Removes all category assignments from a posterminal.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `posterminalId` | `number` | The id of the posterminal. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -122,6 +126,7 @@ Assigns a category to a posterminal (turns it on for that category).
| `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. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -150,6 +155,7 @@ Assigns all categories of the user's selected company to a posterminal.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `posterminalId` | `number` | The id of the posterminal. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -182,6 +188,7 @@ Creates or updates a posterminal for the user's selected company.
| `input.pairingpin` | `string` | The pairing pin. |
| `[input.token]` | `string` | Optional token. |
| `[input.id]` | `number` | Optional id to update an existing posterminal. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

View File

@@ -19,6 +19,7 @@ Creates a new quote for the user's selected company with its lines.
| `[body.entityId]` | `number` | The id of the customer entity. |
| `[body.externalNumber]` | `string` | An external (customer) reference number. |
| `[body.lines]` | `Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>` | The quote lines. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -47,6 +48,7 @@ Deletes a quote by its id within the user's selected company.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `id` | `number` | The id of the quote to delete. |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -74,6 +76,7 @@ Returns a single quote by its id within the user's selected company.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `id` | `number` | The id of the quote. |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -101,6 +104,7 @@ Lists the quotes of the user's selected company.
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `[tx]` | `object` | Optional transaction. |
**Returns**
@@ -131,6 +135,7 @@ Updates a quote's header and/or lines.
| `[patch.entityId]` | `number` | The new customer entity id. |
| `[patch.externalNumber]` | `string` | The new external (customer) reference number. |
| `[patch.lines]` | `Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>` | The new quote lines. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

20
API.runner.js.md Normal file
View File

@@ -0,0 +1,20 @@
# runner.js
This document is auto-generated from the JSDoc comments in `src/store` by running `npm run api-doc`.
## `runner.js.runner`
*Source: `src/store/runner.js`*
Resolves the object a store method should query through. Converted store modules call `runner(sql, params)` or `runner.query(sql, params)`. During the incremental migration they may still be constructed with a raw `mssql` pool, or called with a raw `mssql` Transaction as `tx`. This adapter exposes the unified runner contract over both so mixed call sites keep working until every module is converted.
**Parameters**
| Name | Type | Description |
| --- | --- | --- |
| `provider` | `import('../providers/provider.js').DatabaseProvider` | |
| `[tx]` | `object` | Optional provider runner or legacy mssql Transaction. |
**Returns**
`((sql: string, params?: object) => Promise<import('../providers/provider.js').QueryResult>) & { query: Function, dialect?: string }`

View File

@@ -15,6 +15,7 @@ Creates a new supplier for the user's selected company, recording history.
| `googleAuthId` | `number` | The id of the Google auth user. |
| `input` | `object` | The supplier input. |
| `input.name` | `string` | The supplier name. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -43,6 +44,7 @@ Deletes a supplier by its id within the user's selected company.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `id` | `number` | The id of the supplier to delete. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -69,6 +71,7 @@ Lists the suppliers of the user's selected company.
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -95,6 +98,7 @@ Updates the name of a supplier, recording history.
| `id` | `number` | The id of the supplier to update. |
| `input` | `object` | The update input. |
| `input.name` | `string` | The new supplier name. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

View File

@@ -14,6 +14,7 @@ Deletes a unit for the user's selected company by its SI code.
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `si` | `string` | The SI code of the unit to delete. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -40,6 +41,7 @@ Lists the units available for the user's selected company.
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
@@ -69,6 +71,7 @@ Creates or updates a unit for the user's selected company, keyed by its SI code.
| `input.basesifactor` | `number` | The base SI factor (must be > 0). |
| `input.symbol` | `string` | The unit symbol. |
| `input.name` | `string` | The unit name. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**

View File

@@ -434,7 +434,16 @@ Errors: `No company selected`, `Company not found`, `Not authorized`, `Order not
## `ledger`
Append-only SQL Server ledger table (`WITH (LEDGER = ON (APPEND_ONLY = ON))`). Rows cannot be updated or deleted. References `manifestarticle` with `ON DELETE NO ACTION` (a line with ledger entries cannot be removed). No store API yet — INSERT only via future store methods / direct SQL.
Append-only SQL Server ledger table (`WITH (LEDGER = ON (APPEND_ONLY = ON))`). Rows cannot be updated or deleted. References `manifestarticle` with `ON DELETE NO ACTION` (a line with ledger entries cannot be removed). Auth via selected company (`google_auth.selected_company` + membership; line must belong to company).
| Method | Signature | Returns |
| --- | --- | --- |
| `add` | `(googleAuthId, { manifestarticle_id, transaction_type, amount, transaction_text })` | `{ id, manifestarticle_id, transaction_type, amount, transaction_text, hash_version, prev_hash, created_at }` |
| `listByManifestarticleId` | `(googleAuthId, manifestarticleId, [transactionType])` | `{ id, manifestarticle_id, sku, transaction_type, amount, transaction_text, hash_version, prev_hash, created_at }[]` |
| `listBySku` | `(googleAuthId, sku, [transactionType])` | `{ id, manifestarticle_id, sku, transaction_type, amount, transaction_text, hash_version, prev_hash, created_at }[]` |
- `hash_version`: 1
- `prev_hash`: For the first entry in the database, `TODO_TSE_BASED_INITHASH`. For subsequent entries, SHA-512 hex of canonical JSON `JSON.stringify({ id, manifestarticle_id, transaction_type, amount, transaction_text, hash_version, prev_hash, created_at })` of the entry with the highest `id`.
| Column | Type | Notes |
| --- | --- | --- |
@@ -443,8 +452,9 @@ Append-only SQL Server ledger table (`WITH (LEDGER = ON (APPEND_ONLY = ON))`). R
| `transaction_type` | `NVARCHAR(100)` | |
| `amount` | `FLOAT` | |
| `transaction_text` | `NVARCHAR(MAX)` | |
| `hash_version` | `INT` | hash algorithm / payload version |
| `prev_hash` | `NVARCHAR(128)` | nullable (genesis row) |
| `hash_version` | `INT` | hash algorithm / payload version (1) |
| `prev_hash` | `NVARCHAR(128)` | SHA-512 hex of previous row or init hash |
| `created_at` | `DATETIME2` | Creation timestamp |
Engine also maintains `ledger_start_transaction_id` / `ledger_start_sequence_number`. `purge`/`reset-db` soft-drops the table (`MSSQL_DroppedLedgerTable_*` remains; a new `ledger` can still be created).

View File

@@ -28,6 +28,7 @@ const MODULE_ORDER = [
'quote',
'order',
'manifestattachment',
'ledger',
'posterminal',
'google_auth',
'pic',

View File

@@ -62,7 +62,7 @@ digraph schema {
style="rounded,filled"; fillcolor="#202a3a"; color="#3f4c5f";
fontcolor="#e2e8f0"; margin=14;
manifestarticle [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#3f4c5f"><TR><TD COLSPAN="2" BGCOLOR="#33415c" ALIGN="CENTER" CELLPADDING="1" BORDER="1"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD PORT="in0" BGCOLOR="#33415c" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD></TR><TR><TD COLSPAN="1" ALIGN="CENTER" CELLPADDING="3"><FONT COLOR="#e2e8f0" POINT-SIZE="12"><B>manifestarticle</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="manifest_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>manifest_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="sku" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">sku?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(100)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="name" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">name?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(255)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="pu" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">pu</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">FLOAT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="amount" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">amount</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">FLOAT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="net" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">net</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">FLOAT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="comment" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">comment?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(MAX)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="rebate" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">rebate</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">FLOAT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="taxrate" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">taxrate</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">FLOAT</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
ledger [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#3f4c5f"><TR><TD COLSPAN="2" BGCOLOR="#33415c" ALIGN="CENTER" CELLPADDING="4" BORDER="1"><FONT COLOR="#e2e8f0" POINT-SIZE="12"><B>ledger</B></FONT></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="manifestarticle_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>manifestarticle_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="transaction_type" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">transaction_type</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(100)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="amount" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">amount</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">FLOAT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="transaction_text" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">transaction_text</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(MAX)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="hash_version" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">hash_version</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="prev_hash" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">prev_hash?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(128)</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
ledger [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#3f4c5f"><TR><TD COLSPAN="2" BGCOLOR="#33415c" ALIGN="CENTER" CELLPADDING="4" BORDER="1"><FONT COLOR="#e2e8f0" POINT-SIZE="12"><B>ledger</B></FONT></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="manifestarticle_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>manifestarticle_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="transaction_type" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">transaction_type</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(100)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="amount" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">amount</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">FLOAT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="transaction_text" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">transaction_text</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(MAX)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="hash_version" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">hash_version</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="prev_hash" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">prev_hash?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(128)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="created_at" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">created_at</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">DATETIME2</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
manifest [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#3f4c5f"><TR><TD COLSPAN="2" BGCOLOR="#33415c" ALIGN="CENTER" CELLPADDING="1" BORDER="1"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD PORT="in0" BGCOLOR="#33415c" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD><TD PORT="in1" BGCOLOR="#33415c" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD></TR><TR><TD COLSPAN="2" ALIGN="CENTER" CELLPADDING="3"><FONT COLOR="#e2e8f0" POINT-SIZE="12"><B>manifest</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="entity_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>entity_id</B>?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="company_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>company_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="number" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">number</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(100)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="type" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">type</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(100)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="external_number" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">external_number?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(100)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="date" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">date</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">DATETIME2</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
manifestattachment [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#3f4c5f"><TR><TD COLSPAN="2" BGCOLOR="#33415c" ALIGN="CENTER" CELLPADDING="4" BORDER="1"><FONT COLOR="#e2e8f0" POINT-SIZE="12"><B>manifestattachment</B></FONT></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="manifest_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>manifest_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="file_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>file_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="sort" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">sort</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
}

View File

@@ -31,8 +31,8 @@
</g>
<g id="clust5" class="cluster">
<title>cluster_manifests</title>
<path fill="#202a3a" stroke="#3f4c5f" d="M250,-519.6C250,-519.6 638,-519.6 638,-519.6 644,-519.6 650,-525.6 650,-531.6 650,-531.6 650,-1603.4 650,-1603.4 650,-1609.4 644,-1615.4 638,-1615.4 638,-1615.4 250,-1615.4 250,-1615.4 244,-1615.4 238,-1609.4 238,-1603.4 238,-1603.4 238,-531.6 238,-531.6 238,-525.6 244,-519.6 250,-519.6"/>
<text xml:space="preserve" text-anchor="middle" x="283.93" y="-1597.9" font-family="Helvetica,sans-Serif" font-size="15.00" fill="#e2e8f0">Documents</text>
<path fill="#202a3a" stroke="#3f4c5f" d="M250,-519.6C250,-519.6 638,-519.6 638,-519.6 644,-519.6 650,-525.6 650,-531.6 650,-531.6 650,-1616.8 650,-1616.8 650,-1622.8 644,-1628.8 638,-1628.8 638,-1628.8 250,-1628.8 250,-1628.8 244,-1628.8 238,-1622.8 238,-1616.8 238,-1616.8 238,-531.6 238,-531.6 238,-525.6 244,-519.6 250,-519.6"/>
<text xml:space="preserve" text-anchor="middle" x="283.93" y="-1611.3" font-family="Helvetica,sans-Serif" font-size="15.00" fill="#e2e8f0">Documents</text>
</g>
<g id="clust6" class="cluster">
<title>cluster_commerce</title>
@@ -1754,45 +1754,49 @@
<!-- ledger -->
<g id="node34" class="node">
<title>ledger</title>
<polygon fill="#33415c" stroke="none" points="339.58,-1547 339.58,-1571.4 532.42,-1571.4 532.42,-1547 339.58,-1547"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1547 339.58,-1571.4 532.42,-1571.4 532.42,-1547 339.58,-1547"/>
<text xml:space="preserve" text-anchor="start" x="417.99" y="-1556.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00" fill="#e2e8f0">ledger</text>
<polygon fill="#2b3a52" stroke="none" points="339.58,-1520.2 339.58,-1547 532.42,-1547 532.42,-1520.2 339.58,-1520.2"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1520.2 339.58,-1547 532.42,-1547 532.42,-1520.2 339.58,-1520.2"/>
<text xml:space="preserve" text-anchor="start" x="344.58" y="-1530.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">id</text>
<text xml:space="preserve" text-anchor="start" x="497.92" y="-1531.9" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT </text>
<text xml:space="preserve" text-anchor="start" x="514.92" y="-1531.9" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="9.00" fill="#8b95a7">PK</text>
<polygon fill="#2b3a52" stroke="none" points="339.58,-1493.4 339.58,-1520.2 532.42,-1520.2 532.42,-1493.4 339.58,-1493.4"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1493.4 339.58,-1520.2 532.42,-1520.2 532.42,-1493.4 339.58,-1493.4"/>
<text xml:space="preserve" text-anchor="start" x="344.58" y="-1503.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">manifestarticle_id</text>
<text xml:space="preserve" text-anchor="start" x="498.42" y="-1505.1" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT </text>
<text xml:space="preserve" text-anchor="start" x="515.42" y="-1505.1" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="9.00" fill="#8b95a7">FK</text>
<polygon fill="#2a3242" stroke="none" points="339.58,-1466.6 339.58,-1493.4 532.42,-1493.4 532.42,-1466.6 339.58,-1466.6"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1466.6 339.58,-1493.4 532.42,-1493.4 532.42,-1466.6 339.58,-1466.6"/>
<text xml:space="preserve" text-anchor="start" x="344.58" y="-1474.4" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">transaction_type</text>
<text xml:space="preserve" text-anchor="start" x="455.9" y="-1476.4" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">NVARCHAR(100)</text>
<polygon fill="#272e3d" stroke="none" points="339.58,-1439.8 339.58,-1466.6 532.42,-1466.6 532.42,-1439.8 339.58,-1439.8"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1439.8 339.58,-1466.6 532.42,-1466.6 532.42,-1439.8 339.58,-1439.8"/>
<text xml:space="preserve" text-anchor="start" x="344.58" y="-1447.6" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">amount</text>
<text xml:space="preserve" text-anchor="start" x="498.41" y="-1449.6" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">FLOAT</text>
<polygon fill="#2a3242" stroke="none" points="339.58,-1413 339.58,-1439.8 532.42,-1439.8 532.42,-1413 339.58,-1413"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1413 339.58,-1439.8 532.42,-1439.8 532.42,-1413 339.58,-1413"/>
<text xml:space="preserve" text-anchor="start" x="344.58" y="-1420.8" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">transaction_text</text>
<text xml:space="preserve" text-anchor="start" x="451.42" y="-1422.8" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">NVARCHAR(MAX)</text>
<polygon fill="#272e3d" stroke="none" points="339.58,-1386.2 339.58,-1413 532.42,-1413 532.42,-1386.2 339.58,-1386.2"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1386.2 339.58,-1413 532.42,-1413 532.42,-1386.2 339.58,-1386.2"/>
<text xml:space="preserve" text-anchor="start" x="344.58" y="-1394" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">hash_version</text>
<text xml:space="preserve" text-anchor="start" x="512.92" y="-1396" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT</text>
<polygon fill="#2a3242" stroke="none" points="339.58,-1359.4 339.58,-1386.2 532.42,-1386.2 532.42,-1359.4 339.58,-1359.4"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1359.4 339.58,-1386.2 532.42,-1386.2 532.42,-1359.4 339.58,-1359.4"/>
<text xml:space="preserve" text-anchor="start" x="344.58" y="-1367.2" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">prev_hash?</text>
<text xml:space="preserve" text-anchor="start" x="455.9" y="-1369.2" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">NVARCHAR(128)</text>
<polygon fill="#33415c" stroke="none" points="339.58,-1560.4 339.58,-1584.8 532.42,-1584.8 532.42,-1560.4 339.58,-1560.4"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1560.4 339.58,-1584.8 532.42,-1584.8 532.42,-1560.4 339.58,-1560.4"/>
<text xml:space="preserve" text-anchor="start" x="417.99" y="-1570" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00" fill="#e2e8f0">ledger</text>
<polygon fill="#2b3a52" stroke="none" points="339.58,-1533.6 339.58,-1560.4 532.42,-1560.4 532.42,-1533.6 339.58,-1533.6"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1533.6 339.58,-1560.4 532.42,-1560.4 532.42,-1533.6 339.58,-1533.6"/>
<text xml:space="preserve" text-anchor="start" x="344.58" y="-1543.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">id</text>
<text xml:space="preserve" text-anchor="start" x="497.92" y="-1545.3" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT </text>
<text xml:space="preserve" text-anchor="start" x="514.92" y="-1545.3" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="9.00" fill="#8b95a7">PK</text>
<polygon fill="#2b3a52" stroke="none" points="339.58,-1506.8 339.58,-1533.6 532.42,-1533.6 532.42,-1506.8 339.58,-1506.8"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1506.8 339.58,-1533.6 532.42,-1533.6 532.42,-1506.8 339.58,-1506.8"/>
<text xml:space="preserve" text-anchor="start" x="344.58" y="-1517" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">manifestarticle_id</text>
<text xml:space="preserve" text-anchor="start" x="498.42" y="-1518.5" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT </text>
<text xml:space="preserve" text-anchor="start" x="515.42" y="-1518.5" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="9.00" fill="#8b95a7">FK</text>
<polygon fill="#2a3242" stroke="none" points="339.58,-1480 339.58,-1506.8 532.42,-1506.8 532.42,-1480 339.58,-1480"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1480 339.58,-1506.8 532.42,-1506.8 532.42,-1480 339.58,-1480"/>
<text xml:space="preserve" text-anchor="start" x="344.58" y="-1487.8" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">transaction_type</text>
<text xml:space="preserve" text-anchor="start" x="455.9" y="-1489.8" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">NVARCHAR(100)</text>
<polygon fill="#272e3d" stroke="none" points="339.58,-1453.2 339.58,-1480 532.42,-1480 532.42,-1453.2 339.58,-1453.2"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1453.2 339.58,-1480 532.42,-1480 532.42,-1453.2 339.58,-1453.2"/>
<text xml:space="preserve" text-anchor="start" x="344.58" y="-1461" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">amount</text>
<text xml:space="preserve" text-anchor="start" x="498.41" y="-1463" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">FLOAT</text>
<polygon fill="#2a3242" stroke="none" points="339.58,-1426.4 339.58,-1453.2 532.42,-1453.2 532.42,-1426.4 339.58,-1426.4"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1426.4 339.58,-1453.2 532.42,-1453.2 532.42,-1426.4 339.58,-1426.4"/>
<text xml:space="preserve" text-anchor="start" x="344.58" y="-1434.2" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">transaction_text</text>
<text xml:space="preserve" text-anchor="start" x="451.42" y="-1436.2" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">NVARCHAR(MAX)</text>
<polygon fill="#272e3d" stroke="none" points="339.58,-1399.6 339.58,-1426.4 532.42,-1426.4 532.42,-1399.6 339.58,-1399.6"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1399.6 339.58,-1426.4 532.42,-1426.4 532.42,-1399.6 339.58,-1399.6"/>
<text xml:space="preserve" text-anchor="start" x="344.58" y="-1407.4" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">hash_version</text>
<text xml:space="preserve" text-anchor="start" x="512.92" y="-1409.4" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT</text>
<polygon fill="#2a3242" stroke="none" points="339.58,-1372.8 339.58,-1399.6 532.42,-1399.6 532.42,-1372.8 339.58,-1372.8"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1372.8 339.58,-1399.6 532.42,-1399.6 532.42,-1372.8 339.58,-1372.8"/>
<text xml:space="preserve" text-anchor="start" x="344.58" y="-1380.6" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">prev_hash?</text>
<text xml:space="preserve" text-anchor="start" x="455.9" y="-1382.6" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">NVARCHAR(128)</text>
<polygon fill="#272e3d" stroke="none" points="339.58,-1346 339.58,-1372.8 532.42,-1372.8 532.42,-1346 339.58,-1346"/>
<polygon fill="none" stroke="#3f4c5f" points="339.58,-1346 339.58,-1372.8 532.42,-1372.8 532.42,-1346 339.58,-1346"/>
<text xml:space="preserve" text-anchor="start" x="344.58" y="-1353.8" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">created_at</text>
<text xml:space="preserve" text-anchor="start" x="476.91" y="-1355.8" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">DATETIME2</text>
</g>
<!-- ledger&#45;&gt;manifestarticle -->
<g id="edge52" class="edge">
<title>ledger:sw&#45;&gt;manifestarticle:n</title>
<g id="a_edge52"><a xlink:title="FK_ledger_manifestarticle">
<path fill="none" stroke="#f5c15c" stroke-width="1.4" d="M338.58,-1493.4C238.78,-1393.6 335.85,-1316.51 339.87,-1177.98"/>
<path fill="none" stroke="#f5c15c" stroke-width="1.4" d="M338.58,-1506.8C234.56,-1402.77 335.88,-1322.44 339.88,-1177.91"/>
<polygon fill="#f5c15c" stroke="#f5c15c" stroke-width="1.4" points="342.32,-1178.35 339.97,-1171.32 337.42,-1178.28 342.32,-1178.35"/>
</a>
</g>

Before

Width:  |  Height:  |  Size: 208 KiB

After

Width:  |  Height:  |  Size: 208 KiB

220
scripts/test-ledger.js Normal file
View File

@@ -0,0 +1,220 @@
import assert from 'node:assert/strict';
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { createHash } from 'node:crypto';
import LibDashStore from '../src/index.js';
import { BetterSqlite3Provider } from '../src/providers/sqlite3/index.js';
import { INIT_HASH, computePrevHash } from '../src/store/ledger/add.js';
async function run() {
console.log('--- Testing store.ledger (add, created_at, list queries) ---');
const tempDir = mkdtempSync(join(tmpdir(), 'test-ledger-'));
const dbFile = join(tempDir, 'test.db');
const storageDir = join(tempDir, 'storage');
const provider = new BetterSqlite3Provider({
filename: dbFile,
storageDir,
});
await provider.initialize();
const store = await new LibDashStore(provider);
try {
// 1. Setup user and company
const googleAuthId = await store.google_auth.add('ledger-test@example.com');
const company = await store.company.create({
name: 'Ledger Test Corp',
iso639: 'de',
taxrate: 19,
reducedtaxrate: 7,
googleAuthId,
});
await store.google_auth.setSelectedCompany(googleAuthId, company.id);
// Create a delivery manifest with lines
const delivery = await store.delivery.create(googleAuthId, {
externalNumber: 'DEL-1',
date: new Date().toISOString(),
lines: [
{
sku: 'SKU-ABC',
name: 'Ledger Item 1',
pu: 1,
amount: 5,
net: 10,
rebate: 0,
taxrate: 19,
},
{
sku: 'SKU-ABC',
name: 'Ledger Item 1 Batch 2',
pu: 1,
amount: 10,
net: 20,
rebate: 0,
taxrate: 19,
},
],
});
const manifestarticleId1 = delivery.lines[0].id;
const manifestarticleId2 = delivery.lines[1].id;
assert(manifestarticleId1, 'manifestarticle line 1 should have an id');
assert(manifestarticleId2, 'manifestarticle line 2 should have an id');
// 2. Test first row (genesis row)
console.log('1. Inserting genesis ledger row...');
const row1 = await store.ledger.add(googleAuthId, {
manifestarticle_id: manifestarticleId1,
transaction_type: 'GOODS_RECEIPT',
amount: 5,
transaction_text: 'Initial delivery movement',
});
assert.equal(row1.id, 1);
assert.equal(row1.manifestarticle_id, manifestarticleId1);
assert.equal(row1.transaction_type, 'GOODS_RECEIPT');
assert.equal(row1.amount, 5);
assert.equal(row1.transaction_text, 'Initial delivery movement');
assert.equal(row1.hash_version, 1);
assert.equal(row1.prev_hash, INIT_HASH);
assert(row1.created_at, 'created_at should exist');
assert(!Number.isNaN(new Date(row1.created_at).getTime()), 'created_at should be valid date string');
console.log('✓ Row 1 created_at:', row1.created_at);
// Verify manual hash computation of row 1 (including created_at)
const expectedHash2 = computePrevHash(row1);
const manualHash2 = createHash('sha512')
.update(
JSON.stringify({
id: 1,
manifestarticle_id: manifestarticleId1,
transaction_type: 'GOODS_RECEIPT',
amount: 5,
transaction_text: 'Initial delivery movement',
hash_version: 1,
prev_hash: INIT_HASH,
created_at: new Date(row1.created_at).toISOString(),
}),
'utf8',
)
.digest('hex');
assert.equal(expectedHash2, manualHash2);
assert.equal(manualHash2.length, 128);
// 3. Test second row (line 1, different transaction type)
console.log('2. Inserting second ledger row...');
const row2 = await store.ledger.add(googleAuthId, {
manifestarticleId: manifestarticleId1,
transactionType: 'INVENTORY_ADJUST',
amount: -1,
transactionText: 'Damaged item written off',
});
assert.equal(row2.id, 2);
assert.equal(row2.hash_version, 1);
assert.equal(row2.prev_hash, expectedHash2);
assert(row2.created_at, 'row 2 created_at should exist');
console.log('✓ Row 2 prev_hash matches SHA-512 of Row 1 with created_at');
// 4. Test third row (line 2, same SKU SKU-ABC)
console.log('3. Inserting third ledger row on line 2...');
const expectedHash3 = computePrevHash(row2);
const row3 = await store.ledger.add(googleAuthId, {
manifestarticle_id: manifestarticleId2,
transaction_type: 'GOODS_RECEIPT',
amount: 10,
transaction_text: 'Second line goods receipt',
});
assert.equal(row3.id, 3);
assert.equal(row3.hash_version, 1);
assert.equal(row3.prev_hash, expectedHash3);
// 5. Test query by manifestarticle_id
console.log('4. Testing listByManifestarticleId...');
const listLine1All = await store.ledger.listByManifestarticleId(googleAuthId, manifestarticleId1);
assert.equal(listLine1All.length, 2);
assert.equal(listLine1All[0].id, 1);
assert.equal(listLine1All[1].id, 2);
assert.equal(listLine1All[0].sku, 'SKU-ABC');
const listLine1GoodsReceipt = await store.ledger.listByManifestarticleId(
googleAuthId,
manifestarticleId1,
'GOODS_RECEIPT',
);
assert.equal(listLine1GoodsReceipt.length, 1);
assert.equal(listLine1GoodsReceipt[0].id, 1);
const listLine1Adjust = await store.ledger.listByManifestarticleId(
googleAuthId,
manifestarticleId1,
{ transaction_type: 'INVENTORY_ADJUST' },
);
assert.equal(listLine1Adjust.length, 1);
assert.equal(listLine1Adjust[0].id, 2);
const listLine1None = await store.ledger.listByManifestarticleId(
googleAuthId,
manifestarticleId1,
'NON_EXISTENT',
);
assert.equal(listLine1None.length, 0);
// 6. Test query by SKU
console.log('5. Testing listBySku...');
const listSkuAll = await store.ledger.listBySku(googleAuthId, 'SKU-ABC');
assert.equal(listSkuAll.length, 3);
assert.equal(listSkuAll[0].id, 1);
assert.equal(listSkuAll[1].id, 2);
assert.equal(listSkuAll[2].id, 3);
const listSkuGoodsReceipt = await store.ledger.listBySku(
googleAuthId,
'SKU-ABC',
'GOODS_RECEIPT',
);
assert.equal(listSkuGoodsReceipt.length, 2);
assert.equal(listSkuGoodsReceipt[0].id, 1);
assert.equal(listSkuGoodsReceipt[1].id, 3);
const listSkuAdjust = await store.ledger.listBySku(
googleAuthId,
'SKU-ABC',
'INVENTORY_ADJUST',
);
assert.equal(listSkuAdjust.length, 1);
assert.equal(listSkuAdjust[0].id, 2);
const listUnknownSku = await store.ledger.listBySku(googleAuthId, 'UNKNOWN-SKU');
assert.equal(listUnknownSku.length, 0);
// 7. Test validation & company authorization
console.log('6. Testing authorization & validation errors...');
await assert.rejects(
() => store.ledger.listByManifestarticleId(googleAuthId, 999999),
/Manifest article not found/,
);
await assert.rejects(
() => store.ledger.listBySku(googleAuthId, ''),
/Invalid SKU/,
);
console.log('✓ All ledger tests passed successfully!');
} finally {
try {
rmSync(tempDir, { recursive: true, force: true });
} catch {
// ignore
}
}
}
run().catch((err) => {
console.error('Test failed:', err);
process.exit(1);
});

View File

@@ -121,6 +121,9 @@ import manifestattachmentRemove from './store/manifestattachment/remove.js';
import manifestattachmentListByManifestId from './store/manifestattachment/listByManifestId.js';
import manifestattachmentGetById from './store/manifestattachment/getById.js';
import manifestattachmentOrder from './store/manifestattachment/order.js';
import ledgerAdd from './store/ledger/add.js';
import ledgerListByManifestarticleId from './store/ledger/listByManifestarticleId.js';
import ledgerListBySku from './store/ledger/listBySku.js';
export default class LibDashStore {
/**
@@ -331,6 +334,12 @@ export default class LibDashStore {
order: manifestattachmentOrder(provider),
};
this.ledger = {
add: ledgerAdd(provider),
listByManifestarticleId: ledgerListByManifestarticleId(provider),
listBySku: ledgerListBySku(provider),
};
this.purge = storePurge(provider);
this.initialize = storeInitialize(provider);

View File

@@ -0,0 +1,88 @@
export const CHECK_MANIFESTARTICLE_ACCESS = `
SELECT ma.id
FROM manifestarticle ma
INNER JOIN manifest m ON ma.manifest_id = m.id
WHERE ma.id = @manifestarticleId AND m.company_id = @companyId;
`;
export const GET_LATEST_ROW = `
SELECT TOP 1
id,
manifestarticle_id,
transaction_type,
amount,
transaction_text,
hash_version,
prev_hash,
created_at
FROM ledger WITH (UPDLOCK, HOLDLOCK)
ORDER BY id DESC;
`;
export const INSERT_ROW = `
INSERT INTO ledger (
manifestarticle_id,
transaction_type,
amount,
transaction_text,
hash_version,
prev_hash
)
OUTPUT
INSERTED.id,
INSERTED.manifestarticle_id,
INSERTED.transaction_type,
INSERTED.amount,
INSERTED.transaction_text,
INSERTED.hash_version,
INSERTED.prev_hash,
INSERTED.created_at
VALUES (
@manifestarticleId,
@transactionType,
@amount,
@transactionText,
@hashVersion,
@prevHash
);
`;
export const LIST_BY_MANIFESTARTICLE = `
SELECT
l.id,
l.manifestarticle_id,
ma.sku,
l.transaction_type,
l.amount,
l.transaction_text,
l.hash_version,
l.prev_hash,
l.created_at
FROM ledger l
INNER JOIN manifestarticle ma ON l.manifestarticle_id = ma.id
INNER JOIN manifest m ON ma.manifest_id = m.id
WHERE l.manifestarticle_id = @manifestarticleId
AND m.company_id = @companyId
AND (@transactionType IS NULL OR l.transaction_type = @transactionType)
ORDER BY l.id ASC;
`;
export const LIST_BY_SKU = `
SELECT
l.id,
l.manifestarticle_id,
ma.sku,
l.transaction_type,
l.amount,
l.transaction_text,
l.hash_version,
l.prev_hash,
l.created_at
FROM ledger l
INNER JOIN manifestarticle ma ON l.manifestarticle_id = ma.id
INNER JOIN manifest m ON ma.manifest_id = m.id
WHERE ma.sku = @sku
AND m.company_id = @companyId
AND (@transactionType IS NULL OR l.transaction_type = @transactionType)
ORDER BY l.id ASC;
`;

View File

@@ -600,6 +600,7 @@ BEGIN
transaction_text NVARCHAR(MAX) NOT NULL,
hash_version INT NOT NULL,
prev_hash NVARCHAR(128) NULL,
created_at DATETIME2 NOT NULL CONSTRAINT DF_ledger_created_at DEFAULT SYSUTCDATETIME(),
CONSTRAINT FK_ledger_manifestarticle FOREIGN KEY (manifestarticle_id) REFERENCES manifestarticle(id) ON DELETE NO ACTION
) WITH (LEDGER = ON (APPEND_ONLY = ON));
END

View File

@@ -385,6 +385,18 @@ export class BetterSqlite3Provider extends BetterSqlite3Executor {
this.db.exec(script);
}
}
// Ensure created_at exists on ledger table if created previously
try {
const ledgerCols = this.db.pragma('table_info("ledger")').map((c) => c.name);
if (ledgerCols.length > 0 && !ledgerCols.includes('created_at')) {
this.db.exec(
`ALTER TABLE ledger ADD COLUMN created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))`,
);
}
} catch {
// ignore
}
}
/**

View File

@@ -0,0 +1,89 @@
export const CHECK_MANIFESTARTICLE_ACCESS = `
SELECT ma.id
FROM manifestarticle ma
INNER JOIN manifest m ON ma.manifest_id = m.id
WHERE ma.id = @manifestarticleId AND m.company_id = @companyId;
`;
export const GET_LATEST_ROW = `
SELECT
id,
manifestarticle_id,
transaction_type,
amount,
transaction_text,
hash_version,
prev_hash,
created_at
FROM ledger
ORDER BY id DESC
LIMIT 1;
`;
export const INSERT_ROW = `
INSERT INTO ledger (
manifestarticle_id,
transaction_type,
amount,
transaction_text,
hash_version,
prev_hash
)
VALUES (
@manifestarticleId,
@transactionType,
@amount,
@transactionText,
@hashVersion,
@prevHash
)
RETURNING
id,
manifestarticle_id,
transaction_type,
amount,
transaction_text,
hash_version,
prev_hash,
created_at;
`;
export const LIST_BY_MANIFESTARTICLE = `
SELECT
l.id,
l.manifestarticle_id,
ma.sku,
l.transaction_type,
l.amount,
l.transaction_text,
l.hash_version,
l.prev_hash,
l.created_at
FROM ledger l
INNER JOIN manifestarticle ma ON l.manifestarticle_id = ma.id
INNER JOIN manifest m ON ma.manifest_id = m.id
WHERE l.manifestarticle_id = @manifestarticleId
AND m.company_id = @companyId
AND (@transactionType IS NULL OR l.transaction_type = @transactionType)
ORDER BY l.id ASC;
`;
export const LIST_BY_SKU = `
SELECT
l.id,
l.manifestarticle_id,
ma.sku,
l.transaction_type,
l.amount,
l.transaction_text,
l.hash_version,
l.prev_hash,
l.created_at
FROM ledger l
INNER JOIN manifestarticle ma ON l.manifestarticle_id = ma.id
INNER JOIN manifest m ON ma.manifest_id = m.id
WHERE ma.sku = @sku
AND m.company_id = @companyId
AND (@transactionType IS NULL OR l.transaction_type = @transactionType)
ORDER BY l.id ASC;
`;

View File

@@ -432,6 +432,7 @@ CREATE TABLE IF NOT EXISTS ledger (
transaction_text TEXT NOT NULL,
hash_version INTEGER NOT NULL,
prev_hash TEXT,
created_at TEXT NOT NULL DEFAULT (${UTC_NOW}),
FOREIGN KEY (manifestarticle_id) REFERENCES manifestarticle(id) ON DELETE NO ACTION
);
`;

View File

@@ -2,6 +2,8 @@ import * as mssqlQueries from '../../providers/mssql/queries/articlesupplier.js'
import * as sqliteQueries from '../../providers/sqlite3/queries/articlesupplier.js';
import { CREATE as MSSQL_CREATE_PRICE } from '../../providers/mssql/queries/price.js';
import { CREATE as SQLITE_CREATE_PRICE } from '../../providers/sqlite3/queries/price.js';
import { INSERT as MSSQL_INSERT_HISTORY } from '../../providers/mssql/queries/articlehistory.js';
import { INSERT as SQLITE_INSERT_HISTORY } from '../../providers/sqlite3/queries/articlehistory.js';
import resolveArticleBySku from '../article/resolveArticleBySku.js';
import ensureLieferantenGroup from '../supplier/ensureLieferantenGroup.js';
import taxEnsure from '../tax/ensure.js';
@@ -20,6 +22,7 @@ export default function create(provider) {
const queries = isSqlite ? sqliteQueries : mssqlQueries;
const createPriceSql = isSqlite ? SQLITE_CREATE_PRICE : MSSQL_CREATE_PRICE;
const insertHistorySql = isSqlite ? SQLITE_INSERT_HISTORY : MSSQL_INSERT_HISTORY;
/**
* Assigns a supplier to an article, optionally creating a purchase price.
@@ -82,7 +85,11 @@ export default function create(provider) {
}
const runCreate = async (runner) => {
const { articleId, companyId } = await resolve(googleAuthId, sku, runner);
const { articleId, companyId, companyuserId } = await resolve(
googleAuthId,
sku,
runner,
);
await assertSupplier(runner, companyId, entityId);
@@ -153,7 +160,28 @@ export default function create(provider) {
companyId,
});
return shapeArticleSupplier(loadRes.rows[0]);
const supplierRow = loadRes.rows[0];
const supplierName =
supplierRow?.supplierName ?? (entityId ? `Lieferant #${entityId}` : 'Lieferant');
const details = [];
const actualSku = supplierRow?.sku ?? supplierSku;
if (actualSku) {
details.push(`Artikelnr: "${actualSku}"`);
}
const actualPrice = supplierRow?.priceNet ?? netPrice;
if (actualPrice !== null && actualPrice !== undefined) {
details.push(`EK: ${actualPrice}`);
}
const suffix = details.length ? ` (${details.join(', ')})` : '';
await runner.query(insertHistorySql, {
articleId,
companyuserId,
text: `Lieferant "${supplierName}" hinzugefügt${suffix}`,
});
return shapeArticleSupplier(supplierRow);
};
if (tx) {

View File

@@ -1,5 +1,7 @@
import * as mssqlQueries from '../../providers/mssql/queries/articlesupplier.js';
import * as sqliteQueries from '../../providers/sqlite3/queries/articlesupplier.js';
import { INSERT as MSSQL_INSERT_HISTORY } from '../../providers/mssql/queries/articlehistory.js';
import { INSERT as SQLITE_INSERT_HISTORY } from '../../providers/sqlite3/queries/articlehistory.js';
import resolveArticleBySku from '../article/resolveArticleBySku.js';
import makeRunner from '../runner.js';
@@ -7,6 +9,7 @@ export default function del(provider) {
const resolve = resolveArticleBySku(provider);
const isSqlite = provider?.dialect === 'sqlite3';
const queries = isSqlite ? sqliteQueries : mssqlQueries;
const insertHistorySql = isSqlite ? SQLITE_INSERT_HISTORY : MSSQL_INSERT_HISTORY;
/**
* Deletes a supplier assignment and cleans up its associated purchase price.
@@ -27,7 +30,11 @@ export default function del(provider) {
}
const runDelete = async (runner) => {
const { articleId, companyId } = await resolve(googleAuthId, sku, runner);
const { articleId, companyId, companyuserId } = await resolve(
googleAuthId,
sku,
runner,
);
const existingRes = await runner.query(queries.GET_BY_ID_AND_ARTICLE_ID, {
id,
@@ -51,6 +58,21 @@ export default function del(provider) {
articleId,
});
}
const supplierName =
existing.supplierName ?? (existing.entityId ? `Lieferant #${existing.entityId}` : 'Lieferant');
const details = [];
if (existing.sku) {
details.push(`Artikelnr: "${existing.sku}"`);
}
const suffix = details.length ? ` (${details.join(', ')})` : '';
await runner.query(insertHistorySql, {
articleId,
companyuserId,
text: `Lieferant "${supplierName}" entfernt${suffix}`,
});
};
if (tx) {

View File

@@ -2,6 +2,8 @@ import * as mssqlQueries from '../../providers/mssql/queries/articlesupplier.js'
import * as sqliteQueries from '../../providers/sqlite3/queries/articlesupplier.js';
import { CREATE as MSSQL_CREATE_PRICE } from '../../providers/mssql/queries/price.js';
import { CREATE as SQLITE_CREATE_PRICE } from '../../providers/sqlite3/queries/price.js';
import { INSERT as MSSQL_INSERT_HISTORY } from '../../providers/mssql/queries/articlehistory.js';
import { INSERT as SQLITE_INSERT_HISTORY } from '../../providers/sqlite3/queries/articlehistory.js';
import resolveArticleBySku from '../article/resolveArticleBySku.js';
import ensureLieferantenGroup from '../supplier/ensureLieferantenGroup.js';
import taxEnsure from '../tax/ensure.js';
@@ -19,6 +21,7 @@ export default function update(provider) {
const queries = isSqlite ? sqliteQueries : mssqlQueries;
const createPriceSql = isSqlite ? SQLITE_CREATE_PRICE : MSSQL_CREATE_PRICE;
const insertHistorySql = isSqlite ? SQLITE_INSERT_HISTORY : MSSQL_INSERT_HISTORY;
/**
* Updates an existing supplier assignment and its associated purchase price.
@@ -76,7 +79,11 @@ export default function update(provider) {
}
const runUpdate = async (runner) => {
const { articleId, companyId } = await resolve(googleAuthId, sku, runner);
const { articleId, companyId, companyuserId } = await resolve(
googleAuthId,
sku,
runner,
);
const existingRes = await runner.query(queries.GET_BY_ID_AND_ARTICLE_ID, {
id,
@@ -161,6 +168,22 @@ export default function update(provider) {
priceId = priceRes.rows[0]?.id ?? priceRes.insertId;
}
}
} else if (priceId != null && (patch.taxId !== undefined || patch.taxRate !== undefined)) {
let taxId = patch.taxId;
if (taxId == null || !Number.isFinite(taxId)) {
if (patch.taxRate != null && Number.isFinite(patch.taxRate)) {
taxId = await ensureTax(patch.taxRate, runner);
} else {
taxId = existing.priceTaxId;
}
}
await runner.query(queries.UPDATE_PRICE, {
id: priceId,
articleId,
net: existing.priceNet,
taxId,
});
}
const nextSku =
@@ -184,6 +207,70 @@ export default function update(provider) {
priceId,
});
const supplierName =
existing.supplierName ?? (existing.entityId ? `Lieferant #${existing.entityId}` : 'Lieferant');
const historyLines = [];
if (patch.sku !== undefined) {
const oldSku = existing.sku ?? '';
const newSku = nextSku ?? '';
if (oldSku !== newSku) {
historyLines.push(
`Lieferant "${supplierName}": Lieferanten-Artikelnr. geändert von "${oldSku}" auf "${newSku}"`,
);
}
}
if (patch.pu !== undefined && Number(patch.pu) !== Number(existing.pu)) {
historyLines.push(
`Lieferant "${supplierName}": VPE geändert von "${existing.pu}" auf "${nextPu}"`,
);
}
if (patch.stock !== undefined) {
const oldStock = existing.stock != null ? String(existing.stock) : '';
const newStock = nextStock != null ? String(nextStock) : '';
if (oldStock !== newStock) {
historyLines.push(
`Lieferant "${supplierName}": Lagerbestand geändert von "${oldStock}" auf "${newStock}"`,
);
}
}
if (patch.netPrice !== undefined) {
if (patch.netPrice === null) {
if (existing.priceId != null && existing.priceNet != null) {
historyLines.push(
`Lieferant "${supplierName}": Einkaufspreis geändert von "${existing.priceNet}" auf ""`,
);
}
} else {
const oldPrice = existing.priceNet != null ? String(existing.priceNet) : '';
const newPrice = String(patch.netPrice);
if (oldPrice !== newPrice) {
historyLines.push(
`Lieferant "${supplierName}": Einkaufspreis geändert von "${oldPrice}" auf "${newPrice}"`,
);
}
}
}
if (patch.taxRate !== undefined && patch.taxRate !== null && existing.priceTaxRate != null) {
if (Number(existing.priceTaxRate) !== Number(patch.taxRate)) {
historyLines.push(
`Lieferant "${supplierName}": MwSt. geändert von "${existing.priceTaxRate}%" auf "${patch.taxRate}%"`,
);
}
}
if (historyLines.length) {
await runner.query(insertHistorySql, {
articleId,
companyuserId,
text: historyLines.join('\n'),
});
}
const loadRes = await runner.query(queries.GET_BY_ID_AND_ARTICLE_ID, {
id,
articleId,

148
src/store/ledger/add.js Normal file
View File

@@ -0,0 +1,148 @@
import { createHash } from 'node:crypto';
import * as mssqlQueries from '../../providers/mssql/queries/ledger.js';
import * as sqliteQueries from '../../providers/sqlite3/queries/ledger.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
export const INIT_HASH = 'TODO_TSE_BASED_INITHASH';
export function shape(row) {
return {
id: row.id,
manifestarticle_id: row.manifestarticle_id,
transaction_type: row.transaction_type,
amount: row.amount,
transaction_text: row.transaction_text,
hash_version: row.hash_version,
prev_hash: row.prev_hash,
created_at:
row.created_at instanceof Date
? row.created_at.toISOString()
: new Date(row.created_at).toISOString(),
};
}
// Computes the SHA-512 hex hash of a previous ledger entry row
// containing all database columns in canonical JSON format.
export function computePrevHash(row) {
if (!row) {
return INIT_HASH;
}
const createdAt =
row.created_at instanceof Date
? row.created_at.toISOString()
: new Date(row.created_at).toISOString();
const payload = JSON.stringify({
id: row.id,
manifestarticle_id: row.manifestarticle_id,
transaction_type: row.transaction_type,
amount: row.amount,
transaction_text: row.transaction_text,
hash_version: row.hash_version,
prev_hash: row.prev_hash,
created_at: createdAt,
});
return createHash('sha512').update(payload, 'utf8').digest('hex');
}
/**
* Appends a new append-only entry to the ledger table.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {object} input - Ledger entry payload.
* @param {number} input.manifestarticle_id - ID of the referenced manifest line (can also pass manifestarticleId).
* @param {string} input.transaction_type - Transaction type (can also pass transactionType).
* @param {number} input.amount - Movement amount.
* @param {string} input.transaction_text - Transaction memo/details (can also pass transactionText).
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{id: number, manifestarticle_id: number, transaction_type: string, amount: number, transaction_text: string, hash_version: number, prev_hash: string, created_at: string}>} The created ledger row.
* @throws {Error} If user not authorized, company not selected, manifest article not found, or input invalid.
*
* @example
* const entry = await store.ledger.add(googleAuthId, {
* manifestarticle_id: 1,
* transaction_type: 'PAYMENT',
* amount: 100.5,
* transaction_text: 'Invoice paid',
* });
*/
export default function add(provider) {
const access = resolveSelectedCompanyAccess(provider);
const isSqlite = provider?.dialect === 'sqlite3';
const queries = isSqlite ? sqliteQueries : mssqlQueries;
return async (googleAuthId, input = {}, tx) => {
const rawManifestarticleId =
input.manifestarticle_id ?? input.manifestarticleId;
const rawTransactionType =
input.transaction_type ?? input.transactionType;
const rawAmount = input.amount;
const rawTransactionText =
input.transaction_text ?? input.transactionText;
const manifestarticleId = Number(rawManifestarticleId);
if (!Number.isInteger(manifestarticleId) || manifestarticleId <= 0) {
throw new Error('Invalid ledger entry');
}
const transactionType =
typeof rawTransactionType === 'string'
? rawTransactionType.trim()
: '';
if (!transactionType || transactionType.length > 100) {
throw new Error('Invalid ledger entry');
}
const amount = Number(rawAmount);
if (!Number.isFinite(amount)) {
throw new Error('Invalid ledger entry');
}
if (rawTransactionText == null) {
throw new Error('Invalid ledger entry');
}
const transactionText = String(rawTransactionText);
const doAdd = async (runner) => {
const { companyId } = await access(googleAuthId, runner);
const check = await runner.query(queries.CHECK_MANIFESTARTICLE_ACCESS, {
manifestarticleId,
companyId,
});
if (!check.rows[0]) {
throw new Error('Manifest article not found');
}
const latest = await runner.query(queries.GET_LATEST_ROW);
const prevRow = latest.rows[0];
const prevHash = computePrevHash(prevRow);
const inserted = await runner.query(queries.INSERT_ROW, {
manifestarticleId,
transactionType,
amount,
transactionText,
hashVersion: 1,
prevHash,
});
const row = inserted.rows[0];
if (!row) {
throw new Error('Failed to insert ledger entry');
}
return shape(row);
};
if (tx) {
return doAdd(makeRunner(provider, tx));
}
if (typeof provider?.transaction === 'function') {
return provider.transaction(doAdd);
}
return doAdd(makeRunner(provider));
};
}

View File

@@ -0,0 +1,85 @@
import * as mssqlQueries from '../../providers/mssql/queries/ledger.js';
import * as sqliteQueries from '../../providers/sqlite3/queries/ledger.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
function shape(row) {
return {
id: row.id,
manifestarticle_id: row.manifestarticle_id,
sku: row.sku ?? null,
transaction_type: row.transaction_type,
amount: row.amount,
transaction_text: row.transaction_text,
hash_version: row.hash_version,
prev_hash: row.prev_hash,
created_at:
row.created_at instanceof Date
? row.created_at.toISOString()
: new Date(row.created_at).toISOString(),
};
}
/**
* Lists ledger entries for a specific manifest article line, optionally filtered by transaction type.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {number} manifestarticleId - The id of the manifest article line.
* @param {string|object} [optionsOrType] - Transaction type string or options object { transaction_type?: string, transactionType?: string }.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<Array<{id: number, manifestarticle_id: number, sku: string|null, transaction_type: string, amount: number, transaction_text: string, hash_version: number, prev_hash: string, created_at: string}>>}
* @throws {Error} If user not authorized, company not selected, or manifest article not found.
*
* @example
* const entries = await store.ledger.listByManifestarticleId(googleAuthId, 42, 'PAYMENT');
* // => [{ id, manifestarticle_id, sku, transaction_type, amount, transaction_text, hash_version, prev_hash, created_at }]
*/
export default function listByManifestarticleId(provider) {
const access = resolveSelectedCompanyAccess(provider);
const isSqlite = provider?.dialect === 'sqlite3';
const queries = isSqlite ? sqliteQueries : mssqlQueries;
return async (googleAuthId, manifestarticleId, optionsOrType, tx) => {
let transactionType = null;
let explicitTx = tx;
if (typeof optionsOrType === 'string') {
transactionType = optionsOrType.trim() || null;
} else if (typeof optionsOrType === 'object' && optionsOrType !== null) {
if (typeof optionsOrType.query === 'function') {
explicitTx = optionsOrType;
} else {
const rawType =
optionsOrType.transaction_type ?? optionsOrType.transactionType;
if (typeof rawType === 'string') {
transactionType = rawType.trim() || null;
}
}
}
const id = Number(manifestarticleId);
if (!Number.isInteger(id) || id <= 0) {
throw new Error('Invalid manifest article id');
}
const runner = makeRunner(provider, explicitTx);
const { companyId } = await access(googleAuthId, runner);
const check = await runner.query(queries.CHECK_MANIFESTARTICLE_ACCESS, {
manifestarticleId: id,
companyId,
});
if (!check.rows[0]) {
throw new Error('Manifest article not found');
}
const res = await runner.query(queries.LIST_BY_MANIFESTARTICLE, {
manifestarticleId: id,
companyId,
transactionType: transactionType || null,
});
return res.rows.map(shape);
};
}

View File

@@ -0,0 +1,76 @@
import * as mssqlQueries from '../../providers/mssql/queries/ledger.js';
import * as sqliteQueries from '../../providers/sqlite3/queries/ledger.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
function shape(row) {
return {
id: row.id,
manifestarticle_id: row.manifestarticle_id,
sku: row.sku ?? null,
transaction_type: row.transaction_type,
amount: row.amount,
transaction_text: row.transaction_text,
hash_version: row.hash_version,
prev_hash: row.prev_hash,
created_at:
row.created_at instanceof Date
? row.created_at.toISOString()
: new Date(row.created_at).toISOString(),
};
}
/**
* Lists ledger entries for a given article SKU, optionally filtered by transaction type.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {string} sku - The article SKU.
* @param {string|object} [optionsOrType] - Transaction type string or options object { transaction_type?: string, transactionType?: string }.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<Array<{id: number, manifestarticle_id: number, sku: string|null, transaction_type: string, amount: number, transaction_text: string, hash_version: number, prev_hash: string, created_at: string}>>}
* @throws {Error} If user not authorized, company not selected, or SKU invalid.
*
* @example
* const entries = await store.ledger.listBySku(googleAuthId, 'SKU-001', 'DELIVERY');
* // => [{ id, manifestarticle_id, sku, transaction_type, amount, transaction_text, hash_version, prev_hash, created_at }]
*/
export default function listBySku(provider) {
const access = resolveSelectedCompanyAccess(provider);
const isSqlite = provider?.dialect === 'sqlite3';
const queries = isSqlite ? sqliteQueries : mssqlQueries;
return async (googleAuthId, sku, optionsOrType, tx) => {
let transactionType = null;
let explicitTx = tx;
if (typeof optionsOrType === 'string') {
transactionType = optionsOrType.trim() || null;
} else if (typeof optionsOrType === 'object' && optionsOrType !== null) {
if (typeof optionsOrType.query === 'function') {
explicitTx = optionsOrType;
} else {
const rawType =
optionsOrType.transaction_type ?? optionsOrType.transactionType;
if (typeof rawType === 'string') {
transactionType = rawType.trim() || null;
}
}
}
const trimmedSku = typeof sku === 'string' ? sku.trim() : '';
if (!trimmedSku) {
throw new Error('Invalid SKU');
}
const runner = makeRunner(provider, explicitTx);
const { companyId } = await access(googleAuthId, runner);
const res = await runner.query(queries.LIST_BY_SKU, {
sku: trimmedSku,
companyId,
transactionType: transactionType || null,
});
return res.rows.map(shape);
};
}