This commit is contained in:
seb
2026-10-04 11:25:17 +02:00
parent e5ff4ef307
commit 43fa8ea105
24 changed files with 1016 additions and 214 deletions

View File

@@ -100,6 +100,8 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
| [`routes/category/_id/property/_propertyId.md`](routes/category/_id/property/_propertyId.md) | `DELETE /category/{id}/property/{propertyId}` |
| [`routes/company/list.md`](routes/company/list.md) | `GET /company` |
| [`routes/company/create.md`](routes/company/create.md) | `POST /company` |
| [`routes/company/history.md`](routes/company/history.md) | `GET /company/history` |
| [`routes/company/addHistory.md`](routes/company/addHistory.md) | `POST /company/history` |
| [`routes/companysetting/list.md`](routes/companysetting/list.md) | `GET /company/setting` |
| [`routes/companysetting/get.md`](routes/companysetting/get.md) | `GET /company/setting/{key}` |
| [`routes/companysetting/set.md`](routes/companysetting/set.md) | `PUT /company/setting/{key}` |

View File

@@ -3030,6 +3030,66 @@
"responses": {
"200": {
"description": "Deleted successfully"
},
"400": {
"description": "Bad request or no company selected",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "Forbidden",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"404": {
"description": "Property name not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"409": {
"description": "Property name in use",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"500": {
"description": "Server error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
@@ -9435,6 +9495,194 @@
}
}
},
"/company/history": {
"get": {
"tags": [
"Company"
],
"summary": "Get company history",
"description": "Returns `companyhistory` rows for the selected company, newest first (`date` DESC, `id` DESC).\n",
"security": [
{
"cookieAuth": []
}
],
"x-markdown": "company/history.md",
"parameters": [
{
"in": "query",
"name": "limit",
"required": false,
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 1000
},
"description": "Maximum number of entries to return (newest first). Omit for all entries."
}
],
"responses": {
"200": {
"description": "Company history (newest first)",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"history",
"truncated"
],
"properties": {
"history": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CompanyHistoryEntry"
}
},
"truncated": {
"type": "boolean",
"description": "True when a limit was applied and the result set was exactly that size (more entries may exist)."
}
}
}
}
}
},
"400": {
"description": "Invalid limit or no company selected",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "Forbidden",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"500": {
"description": "Server error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
},
"post": {
"tags": [
"Company"
],
"summary": "Add company history entry",
"description": "Appends an entry to `companyhistory` for the selected company.\n",
"security": [
{
"cookieAuth": []
}
],
"x-markdown": "company/addHistory.md",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"text"
],
"properties": {
"text": {
"type": "string",
"description": "History entry text or note"
}
}
}
}
}
},
"responses": {
"201": {
"description": "History entry created",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"history"
],
"properties": {
"history": {
"$ref": "#/components/schemas/CompanyHistoryEntry"
}
}
}
}
}
},
"400": {
"description": "Text required or no company selected",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "Forbidden",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"500": {
"description": "Server error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/company/{id}": {
"put": {
"tags": [
@@ -9837,40 +10085,14 @@
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"attributevalues"
],
"properties": {
"attributevalues": {
"type": "array",
"description": "Array of attribute value IDs (numbers) or objects with id or attributeValueId",
"items": {
"oneOf": [
{
"type": "integer",
"description": "Attribute value ID"
},
{
"type": "object",
"properties": {
"id": {
"type": "integer",
"description": "Attribute value ID"
},
"attributeValueId": {
"type": "integer",
"description": "Attribute value ID"
},
"sort": {
"type": "integer"
}
}
}
]
}
}
}
"type": "array",
"description": "Array of attribute value IDs. Array order defines sort.",
"items": {
"type": "integer"
},
"example": [
166
]
}
}
}
@@ -11305,6 +11527,66 @@
"responses": {
"200": {
"description": "Deleted successfully"
},
"400": {
"description": "Bad request or no company selected",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "Forbidden",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"404": {
"description": "Attribute value not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"409": {
"description": "Attribute value in use",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"500": {
"description": "Server error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
@@ -11521,6 +11803,66 @@
"responses": {
"200": {
"description": "Deleted successfully"
},
"400": {
"description": "Bad request or no company selected",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "Forbidden",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"404": {
"description": "Attribute name not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"409": {
"description": "Attribute name in use",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"500": {
"description": "Server error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
@@ -12339,40 +12681,14 @@
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"attributevalues"
],
"properties": {
"attributevalues": {
"type": "array",
"description": "Array of attribute value IDs (numbers) or objects with id or attributeValueId",
"items": {
"oneOf": [
{
"type": "integer",
"description": "Attribute value ID"
},
{
"type": "object",
"properties": {
"id": {
"type": "integer",
"description": "Attribute value ID"
},
"attributeValueId": {
"type": "integer",
"description": "Attribute value ID"
},
"sort": {
"type": "integer"
}
}
}
]
}
}
}
"type": "array",
"description": "Array of attribute value IDs. Array order defines sort.",
"items": {
"type": "integer"
},
"example": [
166
]
}
}
}
@@ -15711,6 +16027,30 @@
}
}
},
"CompanyHistoryEntry": {
"type": "object",
"required": [
"id",
"date",
"text"
],
"properties": {
"id": {
"type": "integer"
},
"companyuserId": {
"type": "integer",
"nullable": true
},
"date": {
"type": "string",
"format": "date-time"
},
"text": {
"type": "string"
}
}
},
"ArticleSupplierPrice": {
"type": "object",
"required": [

View File

@@ -1,6 +1,6 @@
# Dash API Routes Overview (All)
Overview of all 178 API endpoints across all modules.
Overview of all 180 API endpoints across all modules.
### Parameter Notation
@@ -20,7 +20,7 @@ Overview of all 178 API endpoints across all modules.
| `GET` | `/article/{sku}/articlelist` | `sku:string` | `200 { items: { id, articleId, sku, name, count, sort }[] }` | Get Stückliste (bundle items) for an article |
| `PUT` | `/article/{sku}/articlelist` | `sku:string, body: { items: { articleId?, sku?: string, count?, sort? }[] }` | `200 { items: object[] }` | Set Stückliste (bundle items) for an article |
| `GET` | `/article/{sku}/attributevalue` | `sku:string` | `200 { attributevalues: { id, articleId, attributeId, attributeName, exclusive, attributeValueId, value, sort }[] }` | List article attribute values |
| `PUT` | `/article/{sku}/attributevalue` | `sku:string, body: { attributevalues: any[] }` | `200 { attributevalues: { id, articleId, attributeId, attributeName, exclusive, attributeValueId, value, sort }[] }` | Bulk set article attribute values |
| `PUT` | `/article/{sku}/attributevalue` | `sku:string, body: integer[]` | `200 { attributevalues: { id, articleId, attributeId, attributeName, exclusive, attributeValueId, value, sort }[] }` | Bulk set article attribute values |
| `PUT` | `/article/{sku}/brand` | `sku:string, body: { brandId: integer }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: { sort, fullHash, fullW, fullH, fullSize, thumbW, thumbH, thumbHash }[], eans: string[], prices: { minmultiplier, net, taxRate, customergroupId }[], suppliers: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price }[] } }` | Set or clear article brand by SKU |
| `PUT` | `/article/{sku}/categories` | `sku:string, body: { categoryIds: integer[] }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: { sort, fullHash, fullW, fullH, fullSize, thumbW, thumbH, thumbHash }[], eans: string[], prices: { minmultiplier, net, taxRate, customergroupId }[], suppliers: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price }[] } }` | Replace article categories by SKU |
| `GET` | `/article/{sku}/children` | `sku:string` | `200 { children: { id, sku, name, fathersort, attributeValueId, attributeValue }[] }` | List child articles of a father article |
@@ -72,7 +72,7 @@ Overview of all 178 API endpoints across all modules.
| `PATCH` | `/category/{id}` | `id:integer, body: { name?, parentId? }` | `200 { category: { id, name, slug, parentId, sort } }` | Update category (rename or move) |
| `GET` | `/category/{id}/articles` | `id:integer` | `200 { articles: { id, sku, name, slug, net, taxRate, track_stock, optionsCount, articleListCount, childCount, fullHash, thumbW, thumbH, thumbHash, brand }[] }` | Articles in category |
| `GET` | `/category/{id}/attributevalue` | `id:integer` | `200 { attributevalues: { id, categoryId, attributeId, attributeName, exclusive, attributeValueId, value, sort }[] }` | List category attribute values |
| `PUT` | `/category/{id}/attributevalue` | `id:integer, body: { attributevalues: any[] }` | `200 { attributevalues: { id, categoryId, attributeId, attributeName, exclusive, attributeValueId, value, sort }[] }` | Bulk set category attribute values |
| `PUT` | `/category/{id}/attributevalue` | `id:integer, body: integer[]` | `200 { attributevalues: { id, categoryId, attributeId, attributeName, exclusive, attributeValueId, value, sort }[] }` | Bulk set category attribute values |
| `DELETE` | `/category/{id}/image` | `id:integer` | `200 { category: { id, thumbW, thumbH, thumbHash } }` | Delete category image |
| `POST` | `/category/{id}/image` | `id:integer, image/avif` | `201 { category: { id, thumbW, thumbH, thumbHash } }` | Add category image |
| `GET` | `/category/{id}/property` | `id:integer` | `200 { properties: { id, categoryId, propertyId, name, exclusive, value, sort }[] }` | List category properties |
@@ -80,6 +80,8 @@ Overview of all 178 API endpoints across all modules.
| `DELETE` | `/category/{id}/property/{propertyId}` | `id:integer, propertyId:integer` | `200` | Delete category property |
| `GET` | `/company` | — | `200 { companies: { id, iso639, name, taxrate, reducedtaxrate }[] }` | List companies |
| `POST` | `/company` | `body: { iso639, name, taxrate, reducedtaxrate }` | `201 { company, user }` | Create company |
| `GET` | `/company/history` | `query: limit?:integer` | `200 { history: { id, companyuserId, date, text }[], truncated }` | Get company history |
| `POST` | `/company/history` | `body: { text: string }` | `201 { history: { id, companyuserId, date, text } }` | Add company history entry |
| `GET` | `/company/setting` | — | `200 { settings: { key, value }[] }` | List company settings |
| `GET` | `/company/setting/{key}` | `key:string` | `200 { setting: { key, value } }` | Get company setting by key |
| `PUT` | `/company/setting/{key}` | `key:string, body: { value, frontend? }` | `200 { setting: { key, value } }` | Upsert company setting |

View File

@@ -1,6 +1,6 @@
# Dash API Routes Overview
Overview of all 142 API endpoints across all modules.
Overview of all 144 API endpoints across all modules.
### Parameter Notation
@@ -19,7 +19,7 @@ Overview of all 142 API endpoints across all modules.
| `GET` | `/article/{sku}/articlelist` | `sku:string` | `200 { items: { id, articleId, sku, name, count, sort }[] }` | Get Stückliste (bundle items) for an article |
| `PUT` | `/article/{sku}/articlelist` | `sku:string, body: { items: { articleId?, sku?: string, count?, sort? }[] }` | `200 { items: object[] }` | Set Stückliste (bundle items) for an article |
| `GET` | `/article/{sku}/attributevalue` | `sku:string` | `200 { attributevalues: { id, articleId, attributeId, attributeName, exclusive, attributeValueId, value, sort }[] }` | List article attribute values |
| `PUT` | `/article/{sku}/attributevalue` | `sku:string, body: { attributevalues: any[] }` | `200 { attributevalues: { id, articleId, attributeId, attributeName, exclusive, attributeValueId, value, sort }[] }` | Bulk set article attribute values |
| `PUT` | `/article/{sku}/attributevalue` | `sku:string, body: integer[]` | `200 { attributevalues: { id, articleId, attributeId, attributeName, exclusive, attributeValueId, value, sort }[] }` | Bulk set article attribute values |
| `PUT` | `/article/{sku}/brand` | `sku:string, body: { brandId: integer }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: { sort, fullHash, fullW, fullH, fullSize, thumbW, thumbH, thumbHash }[], eans: string[], prices: { minmultiplier, net, taxRate, customergroupId }[], suppliers: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price }[] } }` | Set or clear article brand by SKU |
| `PUT` | `/article/{sku}/categories` | `sku:string, body: { categoryIds: integer[] }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: { sort, fullHash, fullW, fullH, fullSize, thumbW, thumbH, thumbHash }[], eans: string[], prices: { minmultiplier, net, taxRate, customergroupId }[], suppliers: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price }[] } }` | Replace article categories by SKU |
| `GET` | `/article/{sku}/children` | `sku:string` | `200 { children: { id, sku, name, fathersort, attributeValueId, attributeValue }[] }` | List child articles of a father article |
@@ -67,12 +67,14 @@ Overview of all 142 API endpoints across all modules.
| `PATCH` | `/category/{id}` | `id:integer, body: { name?, parentId? }` | `200 { category: { id, name, slug, parentId, sort } }` | Update category (rename or move) |
| `GET` | `/category/{id}/articles` | `id:integer` | `200 { articles: { id, sku, name, slug, net, taxRate, track_stock, optionsCount, articleListCount, childCount, fullHash, thumbW, thumbH, thumbHash, brand }[] }` | Articles in category |
| `GET` | `/category/{id}/attributevalue` | `id:integer` | `200 { attributevalues: { id, categoryId, attributeId, attributeName, exclusive, attributeValueId, value, sort }[] }` | List category attribute values |
| `PUT` | `/category/{id}/attributevalue` | `id:integer, body: { attributevalues: any[] }` | `200 { attributevalues: { id, categoryId, attributeId, attributeName, exclusive, attributeValueId, value, sort }[] }` | Bulk set category attribute values |
| `PUT` | `/category/{id}/attributevalue` | `id:integer, body: integer[]` | `200 { attributevalues: { id, categoryId, attributeId, attributeName, exclusive, attributeValueId, value, sort }[] }` | Bulk set category attribute values |
| `DELETE` | `/category/{id}/image` | `id:integer` | `200 { category: { id, thumbW, thumbH, thumbHash } }` | Delete category image |
| `GET` | `/category/{id}/property` | `id:integer` | `200 { properties: { id, categoryId, propertyId, name, exclusive, value, sort }[] }` | List category properties |
| `PUT` | `/category/{id}/property` | `id:integer, body: { properties: { propertyId, value, sort? }[] }` | `200 { properties: { id, categoryId, propertyId, name, exclusive, value, sort }[] }` | Bulk set category properties |
| `DELETE` | `/category/{id}/property/{propertyId}` | `id:integer, propertyId:integer` | `200` | Delete category property |
| `GET` | `/company` | — | `200 { companies: { id, iso639, name, taxrate, reducedtaxrate }[] }` | List companies |
| `GET` | `/company/history` | `query: limit?:integer` | `200 { history: { id, companyuserId, date, text }[], truncated }` | Get company history |
| `POST` | `/company/history` | `body: { text: string }` | `201 { history: { id, companyuserId, date, text } }` | Add company history entry |
| `GET` | `/company/setting` | — | `200 { settings: { key, value }[] }` | List company settings |
| `GET` | `/company/setting/{key}` | `key:string` | `200 { setting: { key, value } }` | Get company setting by key |
| `PUT` | `/company/setting/{key}` | `key:string, body: { value, frontend? }` | `200 { setting: { key, value } }` | Upsert company setting |

View File

@@ -14,16 +14,10 @@ Replaces all attribute values for an article. Enforces exclusive constraint (max
## Body (JSON)
| Field | Required | Type | Description |
|-------|----------|------|-------------|
| `attributevalues` | yes | any[] | Array of attribute value IDs (numbers) or objects with id or attributeValueId |
```json
{
"attributevalues": [
"…"
]
}
[
166
]
```
## Response `200`

View File

@@ -16,6 +16,17 @@ Deletes an attribute name and cascades to attribute values and article assignmen
Deleted successfully
## Errors
| Status | Description |
|--------|-------------|
| 400 | Bad request or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Forbidden — `{"error":"…"}` |
| 404 | Attribute name not found — `{"error":"…"}` |
| 409 | Attribute name in use — `{"error":"…"}` |
| 500 | Server error — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -16,6 +16,17 @@ Deletes an attribute value and cascades to article assignments.
Deleted successfully
## Errors
| Status | Description |
|--------|-------------|
| 400 | Bad request or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Forbidden — `{"error":"…"}` |
| 404 | Attribute value not found — `{"error":"…"}` |
| 409 | Attribute value in use — `{"error":"…"}` |
| 500 | Server error — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -14,16 +14,10 @@ Replaces all attribute values for a category. Enforces exclusive constraint (max
## Body (JSON)
| Field | Required | Type | Description |
|-------|----------|------|-------------|
| `attributevalues` | yes | any[] | Array of attribute value IDs (numbers) or objects with id or attributeValueId |
```json
{
"attributevalues": [
"…"
]
}
[
166
]
```
## Response `200`

View File

@@ -0,0 +1,48 @@
# `POST /company/history`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Add company history entry
Appends an entry to `companyhistory` for the selected company.
## Body (JSON)
| Field | Required | Type | Description |
|-------|----------|------|-------------|
| `text` | yes | string | History entry text or note |
```json
{
"text": "…"
}
```
## Response `201`
History entry created
```json
{
"history": {
"id": 1,
"companyuserId": 1,
"date": "2026-08-10T00:00:00.000Z",
"text": "…"
}
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | Text required or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Forbidden — `{"error":"…"}` |
| 500 | Server error — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,45 @@
# `GET /company/history`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Get company history
Returns `companyhistory` rows for the selected company, newest first (`date` DESC, `id` DESC).
## Query parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `limit` | no | integer | Maximum number of entries to return (newest first). Omit for all entries. |
## Response `200`
Company history (newest first)
```json
{
"history": [
{
"id": 1,
"companyuserId": 1,
"date": "2026-08-10T00:00:00.000Z",
"text": "…"
}
],
"truncated": true
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | Invalid limit or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Forbidden — `{"error":"…"}` |
| 500 | Server error — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -16,6 +16,17 @@ Deletes a property name and cascades to article properties.
Deleted successfully
## Errors
| Status | Description |
|--------|-------------|
| 400 | Bad request or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Forbidden — `{"error":"…"}` |
| 404 | Property name not found — `{"error":"…"}` |
| 409 | Property name in use — `{"error":"…"}` |
| 500 | Server error — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._