u
This commit is contained in:
@@ -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/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/list.md`](routes/company/list.md) | `GET /company` |
|
||||||
| [`routes/company/create.md`](routes/company/create.md) | `POST /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/list.md`](routes/companysetting/list.md) | `GET /company/setting` |
|
||||||
| [`routes/companysetting/get.md`](routes/companysetting/get.md) | `GET /company/setting/{key}` |
|
| [`routes/companysetting/get.md`](routes/companysetting/get.md) | `GET /company/setting/{key}` |
|
||||||
| [`routes/companysetting/set.md`](routes/companysetting/set.md) | `PUT /company/setting/{key}` |
|
| [`routes/companysetting/set.md`](routes/companysetting/set.md) | `PUT /company/setting/{key}` |
|
||||||
|
|||||||
@@ -3030,6 +3030,66 @@
|
|||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Deleted successfully"
|
"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}": {
|
"/company/{id}": {
|
||||||
"put": {
|
"put": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -9837,43 +10085,17 @@
|
|||||||
"content": {
|
"content": {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"attributevalues"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"attributevalues": {
|
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"description": "Array of attribute value IDs (numbers) or objects with id or attributeValueId",
|
"description": "Array of attribute value IDs. Array order defines sort.",
|
||||||
"items": {
|
"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": "integer"
|
||||||
}
|
},
|
||||||
}
|
"example": [
|
||||||
}
|
166
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
@@ -11305,6 +11527,66 @@
|
|||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Deleted successfully"
|
"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": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Deleted successfully"
|
"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,43 +12681,17 @@
|
|||||||
"content": {
|
"content": {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"attributevalues"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"attributevalues": {
|
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"description": "Array of attribute value IDs (numbers) or objects with id or attributeValueId",
|
"description": "Array of attribute value IDs. Array order defines sort.",
|
||||||
"items": {
|
"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": "integer"
|
||||||
}
|
},
|
||||||
}
|
"example": [
|
||||||
}
|
166
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
@@ -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": {
|
"ArticleSupplierPrice": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Dash API Routes Overview (All)
|
# 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
|
### 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 |
|
| `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 |
|
| `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 |
|
| `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}/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 |
|
| `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 |
|
| `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) |
|
| `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}/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 |
|
| `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 |
|
| `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 |
|
| `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 |
|
| `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 |
|
| `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` | — | `200 { companies: { id, iso639, name, taxrate, reducedtaxrate }[] }` | List companies |
|
||||||
| `POST` | `/company` | `body: { iso639, name, taxrate, reducedtaxrate }` | `201 { company, user }` | Create company |
|
| `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` | — | `200 { settings: { key, value }[] }` | List company settings |
|
||||||
| `GET` | `/company/setting/{key}` | `key:string` | `200 { setting: { key, value } }` | Get company setting by key |
|
| `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 |
|
| `PUT` | `/company/setting/{key}` | `key:string, body: { value, frontend? }` | `200 { setting: { key, value } }` | Upsert company setting |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Dash API Routes Overview
|
# Dash API Routes Overview
|
||||||
|
|
||||||
Overview of all 142 API endpoints across all modules.
|
Overview of all 144 API endpoints across all modules.
|
||||||
|
|
||||||
### Parameter Notation
|
### 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 |
|
| `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 |
|
| `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 |
|
| `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}/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 |
|
| `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 |
|
| `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) |
|
| `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}/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 |
|
| `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 |
|
| `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 |
|
| `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 |
|
| `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 |
|
| `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` | — | `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` | — | `200 { settings: { key, value }[] }` | List company settings |
|
||||||
| `GET` | `/company/setting/{key}` | `key:string` | `200 { setting: { key, value } }` | Get company setting by key |
|
| `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 |
|
| `PUT` | `/company/setting/{key}` | `key:string, body: { value, frontend? }` | `200 { setting: { key, value } }` | Upsert company setting |
|
||||||
|
|||||||
@@ -14,16 +14,10 @@ Replaces all attribute values for an article. Enforces exclusive constraint (max
|
|||||||
|
|
||||||
## Body (JSON)
|
## Body (JSON)
|
||||||
|
|
||||||
| Field | Required | Type | Description |
|
|
||||||
|-------|----------|------|-------------|
|
|
||||||
| `attributevalues` | yes | any[] | Array of attribute value IDs (numbers) or objects with id or attributeValueId |
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
[
|
||||||
"attributevalues": [
|
166
|
||||||
"…"
|
|
||||||
]
|
]
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|||||||
@@ -16,6 +16,17 @@ Deletes an attribute name and cascades to attribute values and article assignmen
|
|||||||
|
|
||||||
Deleted successfully
|
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`._
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
|
|||||||
@@ -16,6 +16,17 @@ Deletes an attribute value and cascades to article assignments.
|
|||||||
|
|
||||||
Deleted successfully
|
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`._
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
|
|||||||
@@ -14,16 +14,10 @@ Replaces all attribute values for a category. Enforces exclusive constraint (max
|
|||||||
|
|
||||||
## Body (JSON)
|
## Body (JSON)
|
||||||
|
|
||||||
| Field | Required | Type | Description |
|
|
||||||
|-------|----------|------|-------------|
|
|
||||||
| `attributevalues` | yes | any[] | Array of attribute value IDs (numbers) or objects with id or attributeValueId |
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
[
|
||||||
"attributevalues": [
|
166
|
||||||
"…"
|
|
||||||
]
|
]
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|||||||
48
docs/api/routes/company/addHistory.md
Normal file
48
docs/api/routes/company/addHistory.md
Normal 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`._
|
||||||
45
docs/api/routes/company/history.md
Normal file
45
docs/api/routes/company/history.md
Normal 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`._
|
||||||
@@ -16,6 +16,17 @@ Deletes a property name and cascades to article properties.
|
|||||||
|
|
||||||
Deleted successfully
|
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`._
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
|
|||||||
108
package-lock.json
generated
108
package-lock.json
generated
@@ -14,10 +14,10 @@
|
|||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
"cross-env": "^10.1.0",
|
"cross-env": "^10.1.0",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"dotenv": "^18.0.4",
|
"dotenv": "^18.0.5",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"google-auth-library": "^11.1.0",
|
"google-auth-library": "^11.1.0",
|
||||||
"ink": "^7.1.1",
|
"ink": "^8.0.0",
|
||||||
"ink-text-input": "^6.0.0",
|
"ink-text-input": "^6.0.0",
|
||||||
"jsonwebtoken": "^9.0.3",
|
"jsonwebtoken": "^9.0.3",
|
||||||
"jszip": "^3.10.2",
|
"jszip": "^3.10.2",
|
||||||
@@ -41,9 +41,9 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clack/prompts": "^1.8.1",
|
"@clack/prompts": "^1.8.1",
|
||||||
"@hpcc-js/wasm-graphviz": "^1.29.1",
|
"@hpcc-js/wasm-graphviz": "^1.29.2",
|
||||||
"chalk": "^6.0.1",
|
"chalk": "^6.0.1",
|
||||||
"dotenv": "^18.0.4",
|
"dotenv": "^18.0.5",
|
||||||
"pdfconvert-wasm": "^0.3.0",
|
"pdfconvert-wasm": "^0.3.0",
|
||||||
"sharp": "^0.35.5",
|
"sharp": "^0.35.5",
|
||||||
"thumbhash": "^0.1.1"
|
"thumbhash": "^0.1.1"
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"license": "0BSD",
|
"license": "0BSD",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotenv": "^18.0.4"
|
"dotenv": "^18.0.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
@@ -2372,43 +2372,41 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/ink": {
|
"node_modules/ink": {
|
||||||
"version": "7.1.1",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ink/-/ink-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ink/-/ink-8.0.0.tgz",
|
||||||
"integrity": "sha512-Y43xxa1ZSPvpmfLHcN5o+OdP8Rf8ykkNJEuKYOUNZKT8wXVNLFTtEm1nSDMQkfBH+YANF4Xuu0hhZ4ejqAtN2w==",
|
"integrity": "sha512-M2aFwZOqmkFxCwedy6xrP4E+DGoK57iahAFnfTqNyOyJ54O7DZzu027z/NI8EGy44Sw9O9BcbTF4QEUS2GHAtg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alcalzone/ansi-tokenize": "^0.3.0",
|
"@alcalzone/ansi-tokenize": "^0.3.1",
|
||||||
"ansi-escapes": "^7.3.0",
|
"ansi-escapes": "^7.3.0",
|
||||||
"ansi-styles": "^6.2.3",
|
"ansi-styles": "^7.0.0",
|
||||||
"auto-bind": "^5.0.1",
|
"auto-bind": "^5.0.1",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^6.0.1",
|
||||||
"cli-boxes": "^4.0.1",
|
"cli-boxes": "^4.0.1",
|
||||||
"cli-cursor": "^4.0.0",
|
"cli-cursor": "^4.0.0",
|
||||||
"cli-truncate": "^6.0.0",
|
"cli-truncate": "^6.0.0",
|
||||||
"code-excerpt": "^4.0.0",
|
"code-excerpt": "^4.0.0",
|
||||||
"es-toolkit": "^1.45.1",
|
"es-toolkit": "^1.52.0",
|
||||||
"indent-string": "^5.0.0",
|
"indent-string": "^5.0.0",
|
||||||
"is-in-ci": "^2.0.0",
|
"is-in-ci": "^2.0.0",
|
||||||
"patch-console": "^2.0.0",
|
"patch-console": "^2.0.0",
|
||||||
"react-reconciler": "^0.33.0",
|
"quick-lru": "^7.3.0",
|
||||||
"scheduler": "^0.27.0",
|
"react-reconciler": "^0.34.0",
|
||||||
"signal-exit": "^3.0.7",
|
"signal-exit": "^3.0.7",
|
||||||
"slice-ansi": "^9.0.0",
|
|
||||||
"stack-utils": "^2.0.6",
|
"stack-utils": "^2.0.6",
|
||||||
"string-width": "^8.2.0",
|
"string-width": "^8.3.0",
|
||||||
"terminal-size": "^4.0.1",
|
"terminal-size": "^4.0.1",
|
||||||
"type-fest": "^5.5.0",
|
"type-fest": "^5.10.0",
|
||||||
"widest-line": "^6.0.0",
|
"widest-line": "^6.0.0",
|
||||||
"wrap-ansi": "^10.0.0",
|
"wrap-ansi": "^10.0.2",
|
||||||
"ws": "^8.20.0",
|
|
||||||
"yoga-layout": "~3.2.1"
|
"yoga-layout": "~3.2.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22"
|
"node": ">=22"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": ">=19.2.0",
|
"@types/react": ">=19.3.0",
|
||||||
"react": ">=19.2.0",
|
"react": ">=19.3.0",
|
||||||
"react-devtools-core": ">=6.1.2"
|
"react-devtools-core": ">=6.1.2"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
@@ -2461,16 +2459,16 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ink/node_modules/chalk": {
|
"node_modules/ink/node_modules/ansi-styles": {
|
||||||
"version": "5.6.2",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-7.0.0.tgz",
|
||||||
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
|
"integrity": "sha512-kKvt3m4uwzqL0wlkPd09CmljPJGOZZ4D0fP65sqFSvPkMRKhNi+74MgIJ5QxE6SxqB4t4KyUFGg8+n5zjo6hew==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
"node": ">=22"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ipaddr.js": {
|
"node_modules/ipaddr.js": {
|
||||||
@@ -3324,6 +3322,18 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/quick-lru": {
|
||||||
|
"version": "7.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-7.3.0.tgz",
|
||||||
|
"integrity": "sha512-k9lSsjl36EJdK7I06v7APZCbyGT2vMTsYSRX1Q2nbYmnkBqgUhRkAuzH08Ciotteu/PLJmIF2+tti7o3C/ts2g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/range-parser": {
|
"node_modules/range-parser": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz",
|
||||||
@@ -3403,26 +3413,19 @@
|
|||||||
"react": "^19.3.0"
|
"react": "^19.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-dom/node_modules/scheduler": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/react-reconciler": {
|
"node_modules/react-reconciler": {
|
||||||
"version": "0.33.0",
|
"version": "0.34.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.33.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.34.0.tgz",
|
||||||
"integrity": "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==",
|
"integrity": "sha512-eVibvB1fc9j5Qf1KRkiM3GzRPBdjg2P6G6QV2f3P6Eb3OGMxRBhdg2rrUwjbV5JrQaJj8nOa8uajmmJA5tJWjQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.28.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^19.2.0"
|
"react": "^19.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/readable-stream": {
|
"node_modules/readable-stream": {
|
||||||
@@ -3536,9 +3539,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/scheduler": {
|
"node_modules/scheduler": {
|
||||||
"version": "0.27.0",
|
"version": "0.28.0",
|
||||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz",
|
||||||
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
|
"integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/selfsigned": {
|
"node_modules/selfsigned": {
|
||||||
@@ -4216,27 +4219,6 @@
|
|||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/ws": {
|
|
||||||
"version": "8.22.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz",
|
|
||||||
"integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"bufferutil": "^4.0.1",
|
|
||||||
"utf-8-validate": ">=5.0.2"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"bufferutil": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"utf-8-validate": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/y18n": {
|
"node_modules/y18n": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
"cross-env": "^10.1.0",
|
"cross-env": "^10.1.0",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"dotenv": "^18.0.4",
|
"dotenv": "^18.0.5",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"google-auth-library": "^11.1.0",
|
"google-auth-library": "^11.1.0",
|
||||||
"ink": "^7.1.1",
|
"ink": "^8.0.0",
|
||||||
"ink-text-input": "^6.0.0",
|
"ink-text-input": "^6.0.0",
|
||||||
"jsonwebtoken": "^9.0.3",
|
"jsonwebtoken": "^9.0.3",
|
||||||
"jszip": "^3.10.2",
|
"jszip": "^3.10.2",
|
||||||
|
|||||||
@@ -217,6 +217,20 @@
|
|||||||
* format: date-time
|
* format: date-time
|
||||||
* text:
|
* text:
|
||||||
* type: string
|
* type: string
|
||||||
|
* 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:
|
* ArticleSupplierPrice:
|
||||||
* type: object
|
* type: object
|
||||||
* required: [net, taxId]
|
* required: [net, taxId]
|
||||||
|
|||||||
@@ -84,26 +84,11 @@ export function register(app, basePath = '') {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: object
|
|
||||||
* required: [attributevalues]
|
|
||||||
* properties:
|
|
||||||
* attributevalues:
|
|
||||||
* type: array
|
* type: array
|
||||||
* description: Array of attribute value IDs (numbers) or objects with id or attributeValueId
|
* description: Array of attribute value IDs. Array order defines sort.
|
||||||
* items:
|
* 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: integer
|
||||||
|
* example: [166]
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Updated article attribute values
|
* description: Updated article attribute values
|
||||||
@@ -147,13 +132,10 @@ export function register(app, basePath = '') {
|
|||||||
return res.status(400).json({ error: skuParsed.error });
|
return res.status(400).json({ error: skuParsed.error });
|
||||||
}
|
}
|
||||||
|
|
||||||
const valuesInput = req.body?.attributevalues ?? req.body?.attributeValues ?? req.body?.attribute_values ?? req.body?.values ?? req.body;
|
if (!Array.isArray(req.body)) return res.status(400).json({ error: 'attributevalues array required' });
|
||||||
if (!Array.isArray(valuesInput)) {
|
|
||||||
return res.status(400).json({ error: 'attributevalues array required' });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const attributevalues = await db.setByArticleSku(req.user.id, skuParsed.sku, valuesInput);
|
const attributevalues = await db.setByArticleSku(req.user.id, skuParsed.sku, req.body);
|
||||||
emitArticleSignal(req, skuParsed.sku);
|
emitArticleSignal(req, skuParsed.sku);
|
||||||
res.json({ attributevalues });
|
res.json({ attributevalues });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ function attributeErrorStatus(error) {
|
|||||||
if (error.message === 'Name required') return 400;
|
if (error.message === 'Name required') return 400;
|
||||||
if (error.message === 'Invalid attribute name order') return 400;
|
if (error.message === 'Invalid attribute name order') return 400;
|
||||||
if (error.message === 'Invalid id') return 400;
|
if (error.message === 'Invalid id') return 400;
|
||||||
|
if (error.message === 'Attribute name in use' || error.message?.includes('FOREIGN KEY constraint')) return 409;
|
||||||
return 500;
|
return 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,6 +201,42 @@ export function register(app, basePath = '') {
|
|||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Deleted successfully
|
* 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'
|
||||||
*/
|
*/
|
||||||
app.patch(`${basePath}/attributename/:id`, async (req, res) => {
|
app.patch(`${basePath}/attributename/:id`, async (req, res) => {
|
||||||
const selected = requireSelectedCompany(req);
|
const selected = requireSelectedCompany(req);
|
||||||
@@ -213,7 +250,9 @@ export function register(app, basePath = '') {
|
|||||||
emit('attributename', { companyId: selected.companyId });
|
emit('attributename', { companyId: selected.companyId });
|
||||||
res.json({ attributename });
|
res.json({ attributename });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(attributeErrorStatus(error)).json({ error: error.message });
|
const status = attributeErrorStatus(error);
|
||||||
|
if (status === 500) console.error('Update attribute name failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -229,7 +268,12 @@ export function register(app, basePath = '') {
|
|||||||
emit('attributename', { companyId: selected.companyId });
|
emit('attributename', { companyId: selected.companyId });
|
||||||
res.json({ ok: true });
|
res.json({ ok: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(attributeErrorStatus(error)).json({ error: error.message });
|
const status = attributeErrorStatus(error);
|
||||||
|
const message = (error.message === 'Attribute name in use' || error.message?.includes('FOREIGN KEY constraint'))
|
||||||
|
? 'Attribute name in use'
|
||||||
|
: error.message;
|
||||||
|
if (status === 500) console.error('Delete attribute name failed:', error);
|
||||||
|
res.status(status).json({ error: message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ function attributeValueErrorStatus(error) {
|
|||||||
if (error.message === 'Value required') return 400;
|
if (error.message === 'Value required') return 400;
|
||||||
if (error.message === 'Invalid attribute value order') return 400;
|
if (error.message === 'Invalid attribute value order') return 400;
|
||||||
if (error.message === 'Invalid id' || error.message === 'Invalid attributeId') return 400;
|
if (error.message === 'Invalid id' || error.message === 'Invalid attributeId') return 400;
|
||||||
|
if (error.message === 'Attribute value in use' || error.message?.includes('FOREIGN KEY constraint')) return 409;
|
||||||
return 500;
|
return 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,6 +217,42 @@ export function register(app, basePath = '') {
|
|||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Deleted successfully
|
* 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'
|
||||||
*/
|
*/
|
||||||
app.patch(`${basePath}/attributevalue/:id`, async (req, res) => {
|
app.patch(`${basePath}/attributevalue/:id`, async (req, res) => {
|
||||||
const selected = requireSelectedCompany(req);
|
const selected = requireSelectedCompany(req);
|
||||||
@@ -229,7 +266,9 @@ export function register(app, basePath = '') {
|
|||||||
emit('attributename', { companyId: selected.companyId });
|
emit('attributename', { companyId: selected.companyId });
|
||||||
res.json({ attributevalue });
|
res.json({ attributevalue });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(attributeValueErrorStatus(error)).json({ error: error.message });
|
const status = attributeValueErrorStatus(error);
|
||||||
|
if (status === 500) console.error('Update attribute value failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -245,7 +284,12 @@ export function register(app, basePath = '') {
|
|||||||
emit('attributename', { companyId: selected.companyId });
|
emit('attributename', { companyId: selected.companyId });
|
||||||
res.json({ ok: true });
|
res.json({ ok: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(attributeValueErrorStatus(error)).json({ error: error.message });
|
const status = attributeValueErrorStatus(error);
|
||||||
|
const message = (error.message === 'Attribute value in use' || error.message?.includes('FOREIGN KEY constraint'))
|
||||||
|
? 'Attribute value in use'
|
||||||
|
: error.message;
|
||||||
|
if (status === 500) console.error('Delete attribute value failed:', error);
|
||||||
|
res.status(status).json({ error: message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -400,6 +400,7 @@ export function register(app, basePath = '') {
|
|||||||
.map((a) => ({
|
.map((a) => ({
|
||||||
...a,
|
...a,
|
||||||
childCount: Number(a.childCount ?? 0),
|
childCount: Number(a.childCount ?? 0),
|
||||||
|
isFather: Boolean(a.isFather ?? a.is_father),
|
||||||
}));
|
}));
|
||||||
res.status(200).json({ articles });
|
res.status(200).json({ articles });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -85,26 +85,11 @@ export function register(app, basePath = '') {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: object
|
|
||||||
* required: [attributevalues]
|
|
||||||
* properties:
|
|
||||||
* attributevalues:
|
|
||||||
* type: array
|
* type: array
|
||||||
* description: Array of attribute value IDs (numbers) or objects with id or attributeValueId
|
* description: Array of attribute value IDs. Array order defines sort.
|
||||||
* items:
|
* 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: integer
|
||||||
|
* example: [166]
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Updated category attribute values
|
* description: Updated category attribute values
|
||||||
@@ -148,13 +133,10 @@ export function register(app, basePath = '') {
|
|||||||
return res.status(400).json({ error: idParsed.error });
|
return res.status(400).json({ error: idParsed.error });
|
||||||
}
|
}
|
||||||
|
|
||||||
const valuesInput = req.body?.attributevalues ?? req.body?.attributeValues ?? req.body?.attribute_values ?? req.body?.values ?? req.body;
|
if (!Array.isArray(req.body)) return res.status(400).json({ error: 'attributevalues array required' });
|
||||||
if (!Array.isArray(valuesInput)) {
|
|
||||||
return res.status(400).json({ error: 'attributevalues array required' });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const attributevalues = await db.setByCategoryId(req.user.id, idParsed.id, valuesInput);
|
const attributevalues = await db.setByCategoryId(req.user.id, idParsed.id, req.body);
|
||||||
emitCategorySignal(req);
|
emitCategorySignal(req);
|
||||||
res.json({ attributevalues });
|
res.json({ attributevalues });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -32,10 +32,21 @@ function parseCompanyId(id) {
|
|||||||
return { companyId };
|
return { companyId };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function requireSelectedCompany(req) {
|
||||||
|
const id = req.user?.selectedCompanyId;
|
||||||
|
if (id == null) {
|
||||||
|
const err = new Error('No company selected');
|
||||||
|
err.statusCode = 400;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
function companyErrorStatus(error) {
|
function companyErrorStatus(error) {
|
||||||
if (error.message === 'Company not found') return 404;
|
if (error.message === 'Company not found') return 404;
|
||||||
if (error.message === 'Company name not available') return 409;
|
if (error.message === 'Company name not available') return 409;
|
||||||
if (error.message === 'Invalid tax rate') return 400;
|
if (error.message === 'Invalid tax rate' || error.message === 'Text required' || error.message === 'No company selected') return 400;
|
||||||
|
if (error.message === 'Not authorized') return 403;
|
||||||
return 500;
|
return 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,6 +178,170 @@ export function register(app, basePath = '') {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /company/history:
|
||||||
|
* get:
|
||||||
|
* tags: [Company]
|
||||||
|
* summary: Get company history
|
||||||
|
* description: |
|
||||||
|
* Returns `companyhistory` rows for the selected company, newest first (`date` DESC, `id` DESC).
|
||||||
|
* 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.
|
||||||
|
* 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'
|
||||||
|
*/
|
||||||
|
app.get(`${basePath}/company/history`, async (req, res) => {
|
||||||
|
try {
|
||||||
|
requireSelectedCompany(req);
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(error.statusCode || 400).json({ error: error.message });
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawLimit = req.query.limit;
|
||||||
|
let limit = null;
|
||||||
|
if (rawLimit !== undefined) {
|
||||||
|
limit = parseInt(rawLimit, 10);
|
||||||
|
if (!Number.isInteger(limit) || limit < 1 || limit > 1000) {
|
||||||
|
return res.status(400).json({ error: 'limit must be an integer between 1 and 1000' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const history = await db.getCompanyHistory(req.user.id, limit != null ? { limit } : undefined);
|
||||||
|
const truncated = limit != null && history.length === limit;
|
||||||
|
res.status(200).json({ history, truncated });
|
||||||
|
} catch (error) {
|
||||||
|
const status = companyErrorStatus(error);
|
||||||
|
if (status === 500) console.error('Get company history failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post(`${basePath}/company/history`, async (req, res) => {
|
||||||
|
try {
|
||||||
|
requireSelectedCompany(req);
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(error.statusCode || 400).json({ error: error.message });
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = typeof req.body?.text === 'string' ? req.body.text.trim() : '';
|
||||||
|
if (!text) {
|
||||||
|
return res.status(400).json({ error: 'text is required' });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const entry = await db.addCompanyHistory(req.user.id, { text });
|
||||||
|
res.status(201).json({ history: entry });
|
||||||
|
} catch (error) {
|
||||||
|
const status = companyErrorStatus(error);
|
||||||
|
if (status === 500) console.error('Add company history failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @openapi
|
* @openapi
|
||||||
* /company/{id}:
|
* /company/{id}:
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ function propertyErrorStatus(error) {
|
|||||||
if (error.message === 'Name required') return 400;
|
if (error.message === 'Name required') return 400;
|
||||||
if (error.message === 'Invalid property name order') return 400;
|
if (error.message === 'Invalid property name order') return 400;
|
||||||
if (error.message === 'Invalid id') return 400;
|
if (error.message === 'Invalid id') return 400;
|
||||||
|
if (error.message === 'Property name in use' || error.message?.includes('FOREIGN KEY constraint')) return 409;
|
||||||
return 500;
|
return 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,6 +215,42 @@ export function register(app, basePath = '') {
|
|||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Deleted successfully
|
* 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'
|
||||||
*/
|
*/
|
||||||
app.patch(`${basePath}/propertyname/:id`, async (req, res) => {
|
app.patch(`${basePath}/propertyname/:id`, async (req, res) => {
|
||||||
const selected = requireSelectedCompany(req);
|
const selected = requireSelectedCompany(req);
|
||||||
@@ -227,7 +264,9 @@ export function register(app, basePath = '') {
|
|||||||
emit('propertyname', { companyId: selected.companyId });
|
emit('propertyname', { companyId: selected.companyId });
|
||||||
res.json({ propertyname });
|
res.json({ propertyname });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(propertyErrorStatus(error)).json({ error: error.message });
|
const status = propertyErrorStatus(error);
|
||||||
|
if (status === 500) console.error('Update property name failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -243,7 +282,12 @@ export function register(app, basePath = '') {
|
|||||||
emit('propertyname', { companyId: selected.companyId });
|
emit('propertyname', { companyId: selected.companyId });
|
||||||
res.json({ ok: true });
|
res.json({ ok: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(propertyErrorStatus(error)).json({ error: error.message });
|
const status = propertyErrorStatus(error);
|
||||||
|
const message = (error.message === 'Property name in use' || error.message?.includes('FOREIGN KEY constraint'))
|
||||||
|
? 'Property name in use'
|
||||||
|
: error.message;
|
||||||
|
if (status === 500) console.error('Delete property name failed:', error);
|
||||||
|
res.status(status).json({ error: message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,3 +24,13 @@ export async function deleteCompany(companyId) {
|
|||||||
const store = await getStore();
|
const store = await getStore();
|
||||||
await store.company.delete(companyId);
|
await store.company.delete(companyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getCompanyHistory(googleAuthId, opts) {
|
||||||
|
const store = await getStore();
|
||||||
|
return store.company.getHistory(googleAuthId, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addCompanyHistory(googleAuthId, data) {
|
||||||
|
const store = await getStore();
|
||||||
|
return store.company.addHistory(googleAuthId, data);
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { createDashbackRequestTool } from '../tools/dashback-request.js';
|
|||||||
import { createAskUserTool } from '../tools/ask-user.js';
|
import { createAskUserTool } from '../tools/ask-user.js';
|
||||||
import { createJavascriptExecuteTool } from '../tools/javascript.js';
|
import { createJavascriptExecuteTool } from '../tools/javascript.js';
|
||||||
import { createResetVfsTool } from '../tools/reset-vfs.js';
|
import { createResetVfsTool } from '../tools/reset-vfs.js';
|
||||||
|
import { createApiSuggestionsOrIssuesTool } from '../tools/apisuggestionsorissues.js';
|
||||||
|
|
||||||
const KEEP_ALIVE_MS = 20_000;
|
const KEEP_ALIVE_MS = 20_000;
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ function buildSystemPrompt() {
|
|||||||
'2. ask_user: Ask clarifying questions or request decisions from the user if their intent is ambiguous or requires choices. When you call ask_user, execution pauses immediately and waits for the user to provide their response.',
|
'2. ask_user: Ask clarifying questions or request decisions from the user if their intent is ambiguous or requires choices. When you call ask_user, execution pauses immediately and waits for the user to provide their response.',
|
||||||
'3. javascript_execute: Run JavaScript snippets in a sandbox to compute, aggregate, transform, or analyze data. Assign module.exports to return a value. With allow_network: true, snippets can directly call Dashback API routes using fetch() (e.g. await fetch("/article/2")) and search images using search_for_images(query). A sandboxed virtual filesystem (fs / vfs) is available to store and retrieve artifacts across runs.',
|
'3. javascript_execute: Run JavaScript snippets in a sandbox to compute, aggregate, transform, or analyze data. Assign module.exports to return a value. With allow_network: true, snippets can directly call Dashback API routes using fetch() (e.g. await fetch("/article/2")) and search images using search_for_images(query). A sandboxed virtual filesystem (fs / vfs) is available to store and retrieve artifacts across runs.',
|
||||||
'4. reset_vfs: Delete all files, images, and artifacts stored in the virtual filesystem (VFS) to free up storage or clean up.',
|
'4. reset_vfs: Delete all files, images, and artifacts stored in the virtual filesystem (VFS) to free up storage or clean up.',
|
||||||
|
'5. apisuggestionsorissues: Report missing API endpoints, bugs, or issues to be triaged. Automatically writes an entry to the company history table for review.',
|
||||||
'',
|
'',
|
||||||
'OPERATING GUIDELINES:',
|
'OPERATING GUIDELINES:',
|
||||||
'- Refer to the DASHBACK API ROUTES documentation below to find appropriate endpoints, parameters, and HTTP methods.',
|
'- Refer to the DASHBACK API ROUTES documentation below to find appropriate endpoints, parameters, and HTTP methods.',
|
||||||
@@ -404,6 +406,7 @@ export class ConversationManager {
|
|||||||
createAskUserTool(),
|
createAskUserTool(),
|
||||||
createJavascriptExecuteTool({ googleAuthId, companyId: this.companyId }),
|
createJavascriptExecuteTool({ googleAuthId, companyId: this.companyId }),
|
||||||
createResetVfsTool({ googleAuthId, companyId: this.companyId }),
|
createResetVfsTool({ googleAuthId, companyId: this.companyId }),
|
||||||
|
createApiSuggestionsOrIssuesTool({ googleAuthId, companyId: this.companyId }),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
61
src/tools/apisuggestionsorissues.js
Normal file
61
src/tools/apisuggestionsorissues.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { defineTool } from 'libllmorbiter';
|
||||||
|
import * as db from '../db/company.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the apisuggestionsorissues tool allowing the agent to report
|
||||||
|
* missing API endpoints, bugs, or general issues into the company history table.
|
||||||
|
* These reports are then visible to triage agents or administrators.
|
||||||
|
*
|
||||||
|
* @param {{ googleAuthId: number, companyId?: number }} ctx
|
||||||
|
*/
|
||||||
|
export function createApiSuggestionsOrIssuesTool({ googleAuthId, companyId }) {
|
||||||
|
return defineTool({
|
||||||
|
name: 'apisuggestionsorissues',
|
||||||
|
description:
|
||||||
|
'Report a missing API endpoint, feature request, bug, or general issue. ' +
|
||||||
|
'The report is recorded in the company history table for review by the triage agent. ' +
|
||||||
|
'Provide a clear description of the missing functionality or issue.',
|
||||||
|
strict: false,
|
||||||
|
parameters: {
|
||||||
|
type: 'object',
|
||||||
|
additionalProperties: false,
|
||||||
|
required: ['text'],
|
||||||
|
properties: {
|
||||||
|
text: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Detailed description of the missing API, bug, or issue.',
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
enum: ['missing_api', 'bug', 'suggestion', 'issue', 'other'],
|
||||||
|
description: 'Category/type of the report.',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Optional short summary or title.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
invoke: async ({ text, type, title }) => {
|
||||||
|
const trimmedText = typeof text === 'string' ? text.trim() : '';
|
||||||
|
if (!trimmedText) {
|
||||||
|
throw new Error('text is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefixParts = [];
|
||||||
|
if (type) prefixParts.push(`[${type.toUpperCase()}]`);
|
||||||
|
if (title) prefixParts.push(title.trim());
|
||||||
|
|
||||||
|
const formatted = prefixParts.length > 0
|
||||||
|
? `${prefixParts.join(' ')}\n${trimmedText}`
|
||||||
|
: trimmedText;
|
||||||
|
|
||||||
|
const entry = await db.addCompanyHistory(googleAuthId, { text: formatted });
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: 'Recorded in company history for triage agent review',
|
||||||
|
entry,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user