diff --git a/API.article.md b/API.article.md index 7008620..3ff2f3e 100644 --- a/API.article.md +++ b/API.article.md @@ -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` | 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` | 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` | 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` | 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** diff --git a/API.articlesupplier.md b/API.articlesupplier.md index 8eeeca6..4b9cc2b 100644 --- a/API.articlesupplier.md +++ b/API.articlesupplier.md @@ -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` | 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** diff --git a/API.brand.md b/API.brand.md index 4100f96..9fdb566 100644 --- a/API.brand.md +++ b/API.brand.md @@ -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** diff --git a/API.category.md b/API.category.md index 4c57bd6..54e7bbb 100644 --- a/API.category.md +++ b/API.category.md @@ -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` | 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** diff --git a/API.company.md b/API.company.md index 62f58c5..8ee7529 100644 --- a/API.company.md +++ b/API.company.md @@ -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** diff --git a/API.companysetting.md b/API.companysetting.md index 764c230..2781196 100644 --- a/API.companysetting.md +++ b/API.companysetting.md @@ -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** diff --git a/API.companyuser.md b/API.companyuser.md index 4f50347..c1f1788 100644 --- a/API.companyuser.md +++ b/API.companyuser.md @@ -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** diff --git a/API.customergroup.md b/API.customergroup.md index db9cfd9..d8d6361 100644 --- a/API.customergroup.md +++ b/API.customergroup.md @@ -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` | 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** diff --git a/API.delivery.md b/API.delivery.md index fdc1fd5..9af2e6f 100644 --- a/API.delivery.md +++ b/API.delivery.md @@ -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` - -**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>` — 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: [...] } -``` diff --git a/API.entity.md b/API.entity.md index 51cf24f..775948b 100644 --- a/API.entity.md +++ b/API.entity.md @@ -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** diff --git a/API.entityaddress.md b/API.entityaddress.md index ee90533..47e570d 100644 --- a/API.entityaddress.md +++ b/API.entityaddress.md @@ -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** diff --git a/API.entitycontact.md b/API.entitycontact.md index 85bb408..a53636a 100644 --- a/API.entitycontact.md +++ b/API.entitycontact.md @@ -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** diff --git a/API.ledger.md b/API.ledger.md new file mode 100644 index 0000000..7f44d6f --- /dev/null +++ b/API.ledger.md @@ -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>` + +**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>` + +**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 }] +``` diff --git a/API.manifestattachment.md b/API.manifestattachment.md index ce26670..ae45b47 100644 --- a/API.manifestattachment.md +++ b/API.manifestattachment.md @@ -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` | The reorder input or array of IDs. | | `[input.ids]` | `Array` | 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** diff --git a/API.md b/API.md index 4238462..e2b754c 100644 --- a/API.md +++ b/API.md @@ -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` - - `delete(id: number): Promise` - - `getByEmail(email: string): Promise>` + - `add(companyId: number, googleAuthId: number, tx?: object): Promise` + - `delete(id: number, tx?: object): Promise` + - `getByEmail(email: string, tx?: object): Promise>` - [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` + - `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` - `getById(id: number, tx?: object): Promise` - `getByName(name: string, tx?: object): Promise` - - `list(googleAuthId: number): Promise>` - - `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>` + - `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` - - `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` + - `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` - - `getCategoryArticles(googleAuthId: number, categoryId: number): Promise>` - - `getTreeForCompany(googleAuthId: number): Promise<{languageId: number, tree: Array, articles: Array}>` - - `move(input: {googleAuthId: number, id: number, parentId: number}): Promise<{id: number, parentId: number, sort: number}>` - - `order(input: {googleAuthId: number, parentId: number, categoryIds: Array}): Promise<{categoryIds: Array}>` - - `rename(input: {googleAuthId: number, id: number, name: string}): Promise<{id: number, name: string, slug: string}>` + - `getCategoryArticles(googleAuthId: number, categoryId: number, tx?: object): Promise>` + - `getTreeForCompany(googleAuthId: number, tx?: object): Promise<{languageId: number, tree: Array, articles: Array}>` + - `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}, tx?: object): Promise<{categoryIds: Array}>` + - `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` - - `deleteImage(googleAuthId: number, sku: string, fullHash: string): Promise` - - `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>` - - `importRows(input: {googleAuthId: number, rows: Array}): Promise<{imported: Array, skipped: Array, errors: Array}>` - - `listExport(googleAuthId: number, categoryId?: number|null): Promise>` - - `listSearchIndex(googleAuthId: number): Promise, categoryIds: Array}>>` - - `orderImages(googleAuthId: number, sku: string, input: {fullHashes: Array}): Promise<{fullHashes: Array}>` - - `setBrandIdBySku(googleAuthId: number, sku: string, brandId: number|null): Promise` - - `setCategoryIdsBySku(googleAuthId: number, sku: string, categoryIds: Array): Promise` - - `setEansBySku(googleAuthId: number, sku: string, eans: Array): Promise` - - `setFatherBySku(googleAuthId: number, sku: string, fatherSku: string|null): Promise` - - `setPricesBySku(googleAuthId: number, sku: string, prices: Array<{price: number, tax: number, minmultiplier?: number}>): Promise` - - `setWeightBySku(googleAuthId: number, sku: string, weight: number): Promise` - - `updateBySku(googleAuthId: number, sku: string, patch: {name?: string, unitSi?: string, unitamount?: number}): Promise` + - `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` + - `deleteImage(googleAuthId: number, sku: string, fullHash: string, tx?: object): Promise` + - `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>` + - `importRows(input: {googleAuthId: number, rows: Array}, tx?: object): Promise<{imported: Array, skipped: Array, errors: Array}>` + - `listExport(googleAuthId: number, categoryId?: number|null, tx?: object): Promise>` + - `listSearchIndex(googleAuthId: number, tx?: object): Promise, categoryIds: Array}>>` + - `orderImages(googleAuthId: number, sku: string, input: {fullHashes: Array}, tx?: object): Promise<{fullHashes: Array}>` + - `setBrandIdBySku(googleAuthId: number, sku: string, brandId: number|null, tx?: object): Promise` + - `setCategoryIdsBySku(googleAuthId: number, sku: string, categoryIds: Array, tx?: object): Promise` + - `setEansBySku(googleAuthId: number, sku: string, eans: Array, tx?: object): Promise` + - `setFatherBySku(googleAuthId: number, sku: string, fatherSku: string|null, tx?: object): Promise` + - `setPricesBySku(googleAuthId: number, sku: string, prices: Array<{price: number, tax: number, minmultiplier?: number}>, tx?: object): Promise` + - `setWeightBySku(googleAuthId: number, sku: string, weight: number, tx?: object): Promise` + - `updateBySku(googleAuthId: number, sku: string, patch: {name?: string, unitSi?: string, unitamount?: number}, tx?: object): Promise` - [lang](API.lang.md) - `create(iso639: string, tx?: object): Promise` - `delete(id: number): Promise` @@ -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>` - [unit](API.unit.md) - - `delete(googleAuthId: number, si: string): Promise` - - `list(googleAuthId: number): Promise>` - - `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` + - `list(googleAuthId: number, tx?: object): Promise>` + - `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>` - - `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>` + - `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` - - `list(googleAuthId: number): Promise>` - - `upsert(googleAuthId: number, input: {name: string, gsprpinfo?: string}): Promise<{id: number, name: string, gsprpinfo: string|null}>` + - `delete(googleAuthId: number, id: number, tx?: object): Promise` + - `list(googleAuthId: number, tx?: object): Promise>` + - `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` - - `list(googleAuthId: number): Promise>` - - `order(googleAuthId: number, input: {customergroupIds: Array}): Promise<{customergroupIds: Array}>` - - `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` + - `list(googleAuthId: number, tx?: object): Promise>` + - `order(googleAuthId: number, input: {customergroupIds: Array}, tx?: object): Promise<{customergroupIds: Array}>` + - `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` - - `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` - - `list(googleAuthId: number, options?: {customergroupId?: number, role?: string}): Promise>` - - `peekNextNumber(googleAuthId: number): Promise` - - `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` + - `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` + - `list(googleAuthId: number, options?: {customergroupId?: number, role?: string}, tx?: object): Promise>` + - `peekNextNumber(googleAuthId: number, tx?: object): Promise` + - `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` - - `list(googleAuthId: number, entityId: number): Promise>` - - `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` + - `list(googleAuthId: number, entityId: number, tx?: object): Promise>` + - `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` - - `list(googleAuthId: number, entityId: number): Promise>` - - `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` + - `list(googleAuthId: number, entityId: number, tx?: object): Promise>` + - `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` - - `list(googleAuthId: number): Promise>` - - `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` + - `list(googleAuthId: number, tx?: object): Promise>` + - `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` - - `listByArticleSku(googleAuthId: number, sku: string): Promise>` - - `listByEntityAndSkus(googleAuthId: number, entityIdOrOptions: number|{entityId?: number, supplierId?: number, skus?: string[]|string}, maybeSkus?: string[]|string): Promise, articlepic: Array<{sort: number, thumbhash: string, hash: string, thumbw: number, thumbh: number}>, eans: string[], articleean: string[]}}>>` - - `order(googleAuthId: number, sku: string, input: {ids: Array}): Promise<{ids: Array}>` - - `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` + - `listByArticleSku(googleAuthId: number, sku: string, tx?: object): Promise>` + - `listByEntityAndSkus(googleAuthId: number, entityIdOrOptions: number|{entityId?: number, supplierId?: number, skus?: string[]|string}, maybeSkus?: string[]|string, tx?: object): Promise, articlepic: Array<{sort: number, thumbhash: string, hash: string, thumbw: number, thumbh: number}>, eans: string[], articleean: string[]}}>>` + - `order(googleAuthId: number, sku: string, input: {ids: Array}, tx?: object): Promise<{ids: Array}>` + - `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` - - `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>` - - `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` - - `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>` - - `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` + - `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>` + - `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` - - `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>` - - `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` + - `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>` + - `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}): Promise<{ids: Array}>` - - `remove(googleAuthId: number, manifestId: number, id: number): Promise` + - `order(googleAuthId: number, manifestId: number, input: {ids?: Array}, tx?: object): Promise<{ids: Array}>` + - `remove(googleAuthId: number, manifestId: number, id: number, tx?: object): Promise` +- [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>` + - `listBySku(googleAuthId: number, sku: string, optionsOrType?: string|object, tx?: object): Promise>` - [posterminal](API.posterminal.md) - - `delete(googleAuthId: number, id: number): Promise` - - `list(googleAuthId: number): Promise>` - - `setOff(googleAuthId: number, posterminalId: number, categoryId: number): Promise<{id: number, categoryIds: Array}>` - - `setOffAll(googleAuthId: number, posterminalId: number): Promise<{id: number, categoryIds: Array}>` - - `setOn(googleAuthId: number, posterminalId: number, categoryId: number): Promise<{id: number, categoryIds: Array}>` - - `setOnAll(googleAuthId: number, posterminalId: number): Promise<{id: number, categoryIds: Array}>` - - `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` + - `list(googleAuthId: number, tx?: object): Promise>` + - `setOff(googleAuthId: number, posterminalId: number, categoryId: number, tx?: object): Promise<{id: number, categoryIds: Array}>` + - `setOffAll(googleAuthId: number, posterminalId: number, tx?: object): Promise<{id: number, categoryIds: Array}>` + - `setOn(googleAuthId: number, posterminalId: number, categoryId: number, tx?: object): Promise<{id: number, categoryIds: Array}>` + - `setOnAll(googleAuthId: number, posterminalId: number, tx?: object): Promise<{id: number, categoryIds: Array}>` + - `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` - `getByEmail(email: string): Promise` @@ -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` - `setSelectedCompany(googleAuthId: number, companyId: number): Promise` - [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>` - - `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>` + - `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) & { query: Function, dialect?: string }` diff --git a/API.numbers.md b/API.numbers.md index c030a2d..85cdbcb 100644 --- a/API.numbers.md +++ b/API.numbers.md @@ -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** diff --git a/API.order.md b/API.order.md index 88a200a..a5979ce 100644 --- a/API.order.md +++ b/API.order.md @@ -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** diff --git a/API.pic.md b/API.pic.md index 9db5a2d..f4c0d35 100644 --- a/API.pic.md +++ b/API.pic.md @@ -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** diff --git a/API.posterminal.md b/API.posterminal.md index c067486..cfc0d1d 100644 --- a/API.posterminal.md +++ b/API.posterminal.md @@ -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** diff --git a/API.quote.md b/API.quote.md index 750e32a..cbcdcc6 100644 --- a/API.quote.md +++ b/API.quote.md @@ -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** diff --git a/API.runner.js.md b/API.runner.js.md new file mode 100644 index 0000000..12ecb97 --- /dev/null +++ b/API.runner.js.md @@ -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) & { query: Function, dialect?: string }` diff --git a/API.supplier.md b/API.supplier.md index 0a811f9..23c8006 100644 --- a/API.supplier.md +++ b/API.supplier.md @@ -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** diff --git a/API.unit.md b/API.unit.md index bf48e1d..13aed9f 100644 --- a/API.unit.md +++ b/API.unit.md @@ -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** diff --git a/README.md b/README.md index 6a614af..cd8fd8d 100644 --- a/README.md +++ b/README.md @@ -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). diff --git a/generate-api-doc.js b/generate-api-doc.js index c357e44..b30d937 100644 --- a/generate-api-doc.js +++ b/generate-api-doc.js @@ -28,6 +28,7 @@ const MODULE_ORDER = [ 'quote', 'order', 'manifestattachment', + 'ledger', 'posterminal', 'google_auth', 'pic', diff --git a/schema-graph.dot b/schema-graph.dot index 2d610ab..3c97457 100644 --- a/schema-graph.dot +++ b/schema-graph.dot @@ -62,7 +62,7 @@ digraph schema { style="rounded,filled"; fillcolor="#202a3a"; color="#3f4c5f"; fontcolor="#e2e8f0"; margin=14; manifestarticle [label=<
manifestarticle
idINT PK
manifest_idINT FK
sku?NVARCHAR(100)
name?NVARCHAR(255)
puFLOAT
amountFLOAT
netFLOAT
comment?NVARCHAR(MAX)
rebateFLOAT
taxrateFLOAT
>]; - ledger [label=<
ledger
idINT PK
manifestarticle_idINT FK
transaction_typeNVARCHAR(100)
amountFLOAT
transaction_textNVARCHAR(MAX)
hash_versionINT
prev_hash?NVARCHAR(128)
>]; + ledger [label=<
ledger
idINT PK
manifestarticle_idINT FK
transaction_typeNVARCHAR(100)
amountFLOAT
transaction_textNVARCHAR(MAX)
hash_versionINT
prev_hash?NVARCHAR(128)
created_atDATETIME2
>]; manifest [label=<
manifest
idINT PK
entity_id?INT FK
company_idINT FK
numberNVARCHAR(100)
typeNVARCHAR(100)
external_number?NVARCHAR(100)
dateDATETIME2
>]; manifestattachment [label=<
manifestattachment
idINT PK
manifest_idINT FK
file_idINT FK
sortINT
>]; } diff --git a/schema-graph.svg b/schema-graph.svg index e9de242..2671e68 100644 --- a/schema-graph.svg +++ b/schema-graph.svg @@ -31,8 +31,8 @@ cluster_manifests - -Documents + +Documents cluster_commerce @@ -1754,45 +1754,49 @@ ledger - - -ledger - - -id -INT -PK - - -manifestarticle_id -INT -FK - - -transaction_type -NVARCHAR(100) - - -amount -FLOAT - - -transaction_text -NVARCHAR(MAX) - - -hash_version -INT - - -prev_hash? -NVARCHAR(128) + + +ledger + + +id +INT +PK + + +manifestarticle_id +INT +FK + + +transaction_type +NVARCHAR(100) + + +amount +FLOAT + + +transaction_text +NVARCHAR(MAX) + + +hash_version +INT + + +prev_hash? +NVARCHAR(128) + + +created_at +DATETIME2 ledger:sw->manifestarticle:n - + diff --git a/scripts/test-ledger.js b/scripts/test-ledger.js new file mode 100644 index 0000000..4b557df --- /dev/null +++ b/scripts/test-ledger.js @@ -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); +}); diff --git a/src/index.js b/src/index.js index 0c066b6..c1a4292 100644 --- a/src/index.js +++ b/src/index.js @@ -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); diff --git a/src/providers/mssql/queries/ledger.js b/src/providers/mssql/queries/ledger.js new file mode 100644 index 0000000..cb688af --- /dev/null +++ b/src/providers/mssql/queries/ledger.js @@ -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; +`; diff --git a/src/providers/mssql/schema.js b/src/providers/mssql/schema.js index d854c58..84e945f 100644 --- a/src/providers/mssql/schema.js +++ b/src/providers/mssql/schema.js @@ -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 diff --git a/src/providers/sqlite3/index.js b/src/providers/sqlite3/index.js index bb485a2..0c6eecc 100644 --- a/src/providers/sqlite3/index.js +++ b/src/providers/sqlite3/index.js @@ -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 + } } /** diff --git a/src/providers/sqlite3/queries/ledger.js b/src/providers/sqlite3/queries/ledger.js new file mode 100644 index 0000000..88e211a --- /dev/null +++ b/src/providers/sqlite3/queries/ledger.js @@ -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; +`; diff --git a/src/providers/sqlite3/schema.js b/src/providers/sqlite3/schema.js index 81b8ff6..438013e 100644 --- a/src/providers/sqlite3/schema.js +++ b/src/providers/sqlite3/schema.js @@ -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 ); `; diff --git a/src/store/articlesupplier/create.js b/src/store/articlesupplier/create.js index 644c2e5..151fcdd 100644 --- a/src/store/articlesupplier/create.js +++ b/src/store/articlesupplier/create.js @@ -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) { diff --git a/src/store/articlesupplier/delete.js b/src/store/articlesupplier/delete.js index 87ab469..2429106 100644 --- a/src/store/articlesupplier/delete.js +++ b/src/store/articlesupplier/delete.js @@ -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) { diff --git a/src/store/articlesupplier/update.js b/src/store/articlesupplier/update.js index c6c069e..7775b88 100644 --- a/src/store/articlesupplier/update.js +++ b/src/store/articlesupplier/update.js @@ -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, diff --git a/src/store/ledger/add.js b/src/store/ledger/add.js new file mode 100644 index 0000000..40b974f --- /dev/null +++ b/src/store/ledger/add.js @@ -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)); + }; +} diff --git a/src/store/ledger/listByManifestarticleId.js b/src/store/ledger/listByManifestarticleId.js new file mode 100644 index 0000000..cc769fd --- /dev/null +++ b/src/store/ledger/listByManifestarticleId.js @@ -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>} + * @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); + }; +} diff --git a/src/store/ledger/listBySku.js b/src/store/ledger/listBySku.js new file mode 100644 index 0000000..1719dd0 --- /dev/null +++ b/src/store/ledger/listBySku.js @@ -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>} + * @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); + }; +}