9326 lines
249 KiB
JSON
9326 lines
249 KiB
JSON
{
|
||
"openapi": "3.0.3",
|
||
"info": {
|
||
"title": "Dash API",
|
||
"version": "1.0.0",
|
||
"description": "Backend API for Dash — Google SSO auth, MSSQL, session cookies.\n\nBrowser clients must send cookies (`credentials: 'include'`). Sessions use an HTTP-only `token` cookie (`Secure`, `SameSite=Strict`, 24h); HTTPS is required for login."
|
||
},
|
||
"servers": [
|
||
{
|
||
"url": "http://localhost:4208/api",
|
||
"description": "Local default (PORT / .env)"
|
||
}
|
||
],
|
||
"tags": [
|
||
{
|
||
"name": "Auth",
|
||
"description": "Google SSO and session"
|
||
},
|
||
{
|
||
"name": "Health",
|
||
"description": "Connectivity checks"
|
||
},
|
||
{
|
||
"name": "Events",
|
||
"description": "SSE update signals for live clients"
|
||
},
|
||
{
|
||
"name": "Article",
|
||
"description": "Articles under categories"
|
||
},
|
||
{
|
||
"name": "Category",
|
||
"description": "Category tree and membership"
|
||
},
|
||
{
|
||
"name": "Company",
|
||
"description": "Company CRUD"
|
||
},
|
||
{
|
||
"name": "CompanySetting",
|
||
"description": "Company-scoped settings"
|
||
},
|
||
{
|
||
"name": "Unit",
|
||
"description": "Company-scoped units of measure"
|
||
},
|
||
{
|
||
"name": "Brand",
|
||
"description": "Company-scoped brands"
|
||
},
|
||
{
|
||
"name": "Supplier",
|
||
"description": "Company-scoped suppliers (Lieferanten)"
|
||
},
|
||
{
|
||
"name": "Delivery",
|
||
"description": "Company-scoped goods receipts (Lieferungen)"
|
||
},
|
||
{
|
||
"name": "Quote",
|
||
"description": "Company-scoped quotes (Angebote)"
|
||
},
|
||
{
|
||
"name": "Order",
|
||
"description": "Company-scoped sales orders (Aufträge)"
|
||
},
|
||
{
|
||
"name": "Customergroup",
|
||
"description": "Company-scoped customer groups"
|
||
},
|
||
{
|
||
"name": "Entity",
|
||
"description": "Company-scoped entities, addresses, and contacts"
|
||
},
|
||
{
|
||
"name": "Image",
|
||
"description": "Content-addressed AVIF by fullHash"
|
||
},
|
||
{
|
||
"name": "Posterminal",
|
||
"description": "Company-scoped POS terminals"
|
||
}
|
||
],
|
||
"paths": {
|
||
"/unit": {
|
||
"get": {
|
||
"tags": [
|
||
"Unit"
|
||
],
|
||
"summary": "List units",
|
||
"description": "Lists units for the authenticated user's selected company (default-language symbol/name).",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "unit/list.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "Unit list",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"units": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/Unit"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"put": {
|
||
"tags": [
|
||
"Unit"
|
||
],
|
||
"summary": "Upsert unit",
|
||
"description": "Insert or update unit by `si` for the selected company. Upserts `unittext` for the company language.\n`basesifactor` must be finite and `> 0`.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "unit/upsert.md",
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"si",
|
||
"basesi",
|
||
"basesifactor",
|
||
"symbol",
|
||
"name"
|
||
],
|
||
"properties": {
|
||
"si": {
|
||
"type": "string"
|
||
},
|
||
"basesi": {
|
||
"type": "string"
|
||
},
|
||
"basesifactor": {
|
||
"type": "number"
|
||
},
|
||
"symbol": {
|
||
"type": "string"
|
||
},
|
||
"name": {
|
||
"type": "string"
|
||
}
|
||
},
|
||
"example": {
|
||
"si": "l",
|
||
"basesi": "l",
|
||
"basesifactor": 1,
|
||
"symbol": "L",
|
||
"name": "Liter"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Upserted unit",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"unit": {
|
||
"$ref": "#/components/schemas/Unit"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid unit 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"delete": {
|
||
"tags": [
|
||
"Unit"
|
||
],
|
||
"summary": "Delete unit",
|
||
"description": "Deletes `si` for the selected company. Fails with `Unit in use` if articles still reference it.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "unit/delete.md",
|
||
"parameters": [
|
||
{
|
||
"in": "query",
|
||
"name": "si",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"204": {
|
||
"description": "Deleted"
|
||
},
|
||
"400": {
|
||
"description": "Missing si 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company or unit not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"409": {
|
||
"description": "Unit in use",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/supplier": {
|
||
"get": {
|
||
"tags": [
|
||
"Supplier"
|
||
],
|
||
"summary": "List suppliers",
|
||
"description": "Lists suppliers (`entity` rows in the company `Lieferanten` customergroup)\nfor the authenticated user's selected company.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "supplier/list.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "Supplier list",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"suppliers": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/Supplier"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"post": {
|
||
"tags": [
|
||
"Supplier"
|
||
],
|
||
"summary": "Create supplier",
|
||
"description": "Creates a supplier in the company `Lieferanten` group (created on first use).\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "supplier/create.md",
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"name"
|
||
],
|
||
"properties": {
|
||
"name": {
|
||
"type": "string"
|
||
}
|
||
},
|
||
"example": {
|
||
"name": "Großhandel Müller"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"201": {
|
||
"description": "Created supplier",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"supplier": {
|
||
"$ref": "#/components/schemas/Supplier"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Name 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/supplier/{id}": {
|
||
"patch": {
|
||
"tags": [
|
||
"Supplier"
|
||
],
|
||
"summary": "Update supplier",
|
||
"description": "Renames a supplier for the selected company.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "supplier/update.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"name"
|
||
],
|
||
"properties": {
|
||
"name": {
|
||
"type": "string"
|
||
}
|
||
},
|
||
"example": {
|
||
"name": "Großhandel Müller GmbH"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated supplier",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"supplier": {
|
||
"$ref": "#/components/schemas/Supplier"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Name required, invalid id, 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Supplier or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"delete": {
|
||
"tags": [
|
||
"Supplier"
|
||
],
|
||
"summary": "Delete supplier",
|
||
"description": "Deletes the supplier entity for the selected company.\nRelated `entityhistory` cascades; `manifest.entity_id` is set null.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "supplier/delete.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"204": {
|
||
"description": "Deleted"
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid id",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Supplier not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/quote": {
|
||
"get": {
|
||
"tags": [
|
||
"Quote"
|
||
],
|
||
"summary": "List quotes",
|
||
"description": "Lists quotes (`manifest` rows with `type = 'quote'`)\nfor the authenticated user's selected company.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "quote/list.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "Quote list",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"quotes": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/QuoteSummary"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"post": {
|
||
"tags": [
|
||
"Quote"
|
||
],
|
||
"summary": "Create quote",
|
||
"description": "Creates a quote with header and lines in one transaction.\n`date` is the document date (valid-until).\n`entityId` must be a company customer (not Lieferanten/POS), or null.\nEach line requires `sku` or `name`, `amount`, `net`, and `tax` (rate); `pu` defaults to `1`; `rebate` defaults to `0`.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "quote/create.md",
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"number",
|
||
"date",
|
||
"lines"
|
||
],
|
||
"properties": {
|
||
"number": {
|
||
"type": "string"
|
||
},
|
||
"date": {
|
||
"type": "string",
|
||
"format": "date-time",
|
||
"description": "Document date / valid-until"
|
||
},
|
||
"entityId": {
|
||
"type": "integer",
|
||
"nullable": true,
|
||
"description": "Customer entity (manifest.entity_id)"
|
||
},
|
||
"lines": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/ManifestLineInput"
|
||
}
|
||
}
|
||
},
|
||
"example": {
|
||
"number": "Q-2026-001",
|
||
"date": "2026-08-31",
|
||
"entityId": 1,
|
||
"lines": [
|
||
{
|
||
"sku": "1",
|
||
"name": "Sample Article",
|
||
"pu": 1,
|
||
"amount": 10,
|
||
"net": 99.5,
|
||
"tax": 19
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"201": {
|
||
"description": "Created quote",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"quote": {
|
||
"$ref": "#/components/schemas/Quote"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid quote, invalid lines, 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company, entity, or article not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/quote/{id}": {
|
||
"get": {
|
||
"tags": [
|
||
"Quote"
|
||
],
|
||
"summary": "Get quote",
|
||
"description": "Returns a quote with lines for the selected company.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "quote/get.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Quote",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"quote": {
|
||
"$ref": "#/components/schemas/Quote"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid id",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Quote or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"patch": {
|
||
"tags": [
|
||
"Quote"
|
||
],
|
||
"summary": "Update quote",
|
||
"description": "Patches header fields that are present. If `lines` is present, replaces\nall lines; if omitted, leaves lines unchanged. Returns the full quote.\n`date` is the document date (valid-until).\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "quote/update.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"number": {
|
||
"type": "string"
|
||
},
|
||
"date": {
|
||
"type": "string",
|
||
"format": "date-time",
|
||
"description": "Document date / valid-until"
|
||
},
|
||
"entityId": {
|
||
"type": "integer",
|
||
"nullable": true
|
||
},
|
||
"lines": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/ManifestLineInput"
|
||
}
|
||
}
|
||
},
|
||
"example": {
|
||
"number": "Q-2026-001b",
|
||
"lines": []
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated quote",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"quote": {
|
||
"$ref": "#/components/schemas/Quote"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid quote, invalid lines, invalid id, 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Quote, entity, article, or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"delete": {
|
||
"tags": [
|
||
"Quote"
|
||
],
|
||
"summary": "Delete quote",
|
||
"description": "Deletes the quote (`manifestarticle` rows cascade).",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "quote/delete.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"204": {
|
||
"description": "Deleted"
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid id",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Quote not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/posterminal": {
|
||
"get": {
|
||
"tags": [
|
||
"Posterminal"
|
||
],
|
||
"summary": "List POS terminals",
|
||
"description": "Lists POS terminals for the authenticated user's selected company.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "posterminal/list.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "Posterminal list",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"posterminals": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/Posterminal"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"put": {
|
||
"tags": [
|
||
"Posterminal"
|
||
],
|
||
"summary": "Create POS terminal",
|
||
"description": "Creates a new POS terminal for the selected company.\nThe pairingpin is randomly generated server-side and returned in the response.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "posterminal/upsert.md",
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"name"
|
||
],
|
||
"properties": {
|
||
"name": {
|
||
"type": "string"
|
||
}
|
||
},
|
||
"example": {
|
||
"name": "Kassa 1"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Created posterminal with generated pairingpin",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"posterminal": {
|
||
"$ref": "#/components/schemas/Posterminal"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid posterminal 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/posterminal/{id}": {
|
||
"delete": {
|
||
"tags": [
|
||
"Posterminal"
|
||
],
|
||
"summary": "Delete POS terminal",
|
||
"description": "Deletes a POS terminal for the selected company.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "posterminal/delete.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"204": {
|
||
"description": "Deleted"
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Posterminal not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/posterminal/{id}/category/{categoryId}": {
|
||
"put": {
|
||
"tags": [
|
||
"Posterminal"
|
||
],
|
||
"summary": "Enable category on terminal",
|
||
"description": "Toggles a single category's visibility on a terminal (idempotent).",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "posterminal/setOn.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
},
|
||
{
|
||
"in": "path",
|
||
"name": "categoryId",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated category IDs for this terminal",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"categoryIds": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid terminal/category",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Posterminal not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"delete": {
|
||
"tags": [
|
||
"Posterminal"
|
||
],
|
||
"summary": "Disable category on terminal",
|
||
"description": "Removes a single category's visibility on a terminal (idempotent).",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "posterminal/setOff.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
},
|
||
{
|
||
"in": "path",
|
||
"name": "categoryId",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated category IDs for this terminal",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"categoryIds": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid terminal/category",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Posterminal not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/posterminal/{id}/all": {
|
||
"put": {
|
||
"tags": [
|
||
"Posterminal"
|
||
],
|
||
"summary": "Enable all categories on terminal",
|
||
"description": "Walks the company's category tree and inserts any missing links\nfor this terminal. Returns the updated category IDs.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "posterminal/setOnAll.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated category IDs for this terminal",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"categoryIds": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid terminal",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Posterminal not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"delete": {
|
||
"tags": [
|
||
"Posterminal"
|
||
],
|
||
"summary": "Disable all categories on terminal",
|
||
"description": "Removes all category links for this terminal.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "posterminal/setOffAll.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated category IDs for this terminal",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"categoryIds": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid terminal",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Posterminal not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/image/{fullHash}": {
|
||
"get": {
|
||
"tags": [
|
||
"Image"
|
||
],
|
||
"summary": "Full image by hash",
|
||
"description": "Returns the full AVIF by `pic.hash` (SHA-256 hex).\nAuth via article category access. Browser-cacheable (`private`, `immutable`, `ETag`).\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "image/get.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "fullHash",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string",
|
||
"pattern": "^[a-fA-F0-9]{64}$"
|
||
},
|
||
"description": "SHA-256 hex of the full image bytes"
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "AVIF image bytes",
|
||
"headers": {
|
||
"Cache-Control": {
|
||
"schema": {
|
||
"type": "string",
|
||
"example": "private, max-age=31536000, immutable"
|
||
}
|
||
},
|
||
"ETag": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"content": {
|
||
"image/avif": {
|
||
"schema": {
|
||
"type": "string",
|
||
"format": "binary"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"304": {
|
||
"description": "Not modified (If-None-Match matched ETag)"
|
||
},
|
||
"400": {
|
||
"description": "Invalid hash",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Image not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/thumb/{fullHash}": {
|
||
"get": {
|
||
"tags": [
|
||
"Image"
|
||
],
|
||
"summary": "Thumb image by hash",
|
||
"description": "Returns the thumb AVIF for `pic.hash` (fullHash — not thumbhash).\nAuth via article category access. Browser-cacheable (`private`, `immutable`, `ETag`).\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "thumb/get.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "fullHash",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string",
|
||
"pattern": "^[a-fA-F0-9]{64}$"
|
||
},
|
||
"description": "SHA-256 hex of the full image bytes"
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "AVIF thumb bytes",
|
||
"headers": {
|
||
"Cache-Control": {
|
||
"schema": {
|
||
"type": "string",
|
||
"example": "private, max-age=31536000, immutable"
|
||
}
|
||
},
|
||
"ETag": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"content": {
|
||
"image/avif": {
|
||
"schema": {
|
||
"type": "string",
|
||
"format": "binary"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"304": {
|
||
"description": "Not modified (If-None-Match matched ETag)"
|
||
},
|
||
"400": {
|
||
"description": "Invalid hash",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Image not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/order": {
|
||
"get": {
|
||
"tags": [
|
||
"Order"
|
||
],
|
||
"summary": "List orders",
|
||
"description": "Lists sales orders (`manifest` rows with `type = 'order'`)\nfor the authenticated user's selected company.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "order/list.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "Order list",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"orders": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/OrderSummary"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"post": {
|
||
"tags": [
|
||
"Order"
|
||
],
|
||
"summary": "Create order",
|
||
"description": "Creates a sales order with header and lines in one transaction.\n`date` is the document date (expected delivery).\n`entityId` must be a company customer (not Lieferanten/POS), or null.\nEach line requires `sku` or `name`, `amount`, `net`, and `tax` (rate); `pu` defaults to `1`; `rebate` defaults to `0`.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "order/create.md",
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"number",
|
||
"date",
|
||
"lines"
|
||
],
|
||
"properties": {
|
||
"number": {
|
||
"type": "string"
|
||
},
|
||
"date": {
|
||
"type": "string",
|
||
"format": "date-time",
|
||
"description": "Document date / expected delivery"
|
||
},
|
||
"entityId": {
|
||
"type": "integer",
|
||
"nullable": true,
|
||
"description": "Customer entity (manifest.entity_id)"
|
||
},
|
||
"lines": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/ManifestLineInput"
|
||
}
|
||
}
|
||
},
|
||
"example": {
|
||
"number": "O-2026-001",
|
||
"date": "2026-09-15",
|
||
"entityId": 1,
|
||
"lines": [
|
||
{
|
||
"sku": "1",
|
||
"name": "Sample Article",
|
||
"pu": 1,
|
||
"amount": 10,
|
||
"net": 99.5,
|
||
"tax": 19
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"201": {
|
||
"description": "Created order",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"order": {
|
||
"$ref": "#/components/schemas/Order"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid order, invalid lines, 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company, entity, or article not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/order/{id}": {
|
||
"get": {
|
||
"tags": [
|
||
"Order"
|
||
],
|
||
"summary": "Get order",
|
||
"description": "Returns a sales order with lines for the selected company.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "order/get.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Order",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"order": {
|
||
"$ref": "#/components/schemas/Order"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid id",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Order or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"patch": {
|
||
"tags": [
|
||
"Order"
|
||
],
|
||
"summary": "Update order",
|
||
"description": "Patches header fields that are present. If `lines` is present, replaces\nall lines; if omitted, leaves lines unchanged. Returns the full order.\n`date` is the document date (expected delivery).\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "order/update.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"number": {
|
||
"type": "string"
|
||
},
|
||
"date": {
|
||
"type": "string",
|
||
"format": "date-time",
|
||
"description": "Document date / expected delivery"
|
||
},
|
||
"entityId": {
|
||
"type": "integer",
|
||
"nullable": true
|
||
},
|
||
"lines": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/ManifestLineInput"
|
||
}
|
||
}
|
||
},
|
||
"example": {
|
||
"number": "O-2026-001b",
|
||
"lines": []
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated order",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"order": {
|
||
"$ref": "#/components/schemas/Order"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid order, invalid lines, invalid id, 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Order, entity, article, or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"delete": {
|
||
"tags": [
|
||
"Order"
|
||
],
|
||
"summary": "Delete order",
|
||
"description": "Deletes the sales order (`manifestarticle` rows cascade).",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "order/delete.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"204": {
|
||
"description": "Deleted"
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid id",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Order not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/number": {
|
||
"get": {
|
||
"tags": [
|
||
"Number"
|
||
],
|
||
"summary": "List number sequences",
|
||
"description": "Lists the number sequences of the authenticated user's selected company.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "number/list.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "Number sequence list",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"numbers": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/NumberSequence"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/number/{id}": {
|
||
"patch": {
|
||
"tags": [
|
||
"Number"
|
||
],
|
||
"summary": "Update number sequence",
|
||
"description": "Updates sequence settings for the selected company. Only provided fields\nare changed (`prefix`, `padding`, `next`). `prefix` is trimmed and must\nbe at most 50 characters. `padding` must be an integer `>= 0`. `next`\nmust be an integer `>= 1`. If no field is provided, the row is returned\nunchanged.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "number/update.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"prefix": {
|
||
"type": "string"
|
||
},
|
||
"padding": {
|
||
"type": "integer",
|
||
"minimum": 0
|
||
},
|
||
"next": {
|
||
"type": "integer",
|
||
"minimum": 1
|
||
}
|
||
},
|
||
"example": {
|
||
"prefix": "A-",
|
||
"padding": 4,
|
||
"next": 100
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated number sequence",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"number": {
|
||
"$ref": "#/components/schemas/NumberSequence"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid number, invalid id, 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Number sequence or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/health": {
|
||
"get": {
|
||
"tags": [
|
||
"Health"
|
||
],
|
||
"summary": "Database health check",
|
||
"description": "Verifies the database client can establish a connection.",
|
||
"security": [],
|
||
"x-markdown": "health.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "Database reachable",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"ok": {
|
||
"type": "boolean",
|
||
"example": true
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"503": {
|
||
"description": "Database unreachable",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"ok": {
|
||
"type": "boolean",
|
||
"example": false
|
||
},
|
||
"error": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/events": {
|
||
"get": {
|
||
"tags": [
|
||
"Events"
|
||
],
|
||
"summary": "Subscribe to update signals (SSE)",
|
||
"description": "Opens a Server-Sent Events stream for the authenticated user's\n**selected company**. Clients receive named signals when another\nrequest successfully mutates shared data for that company\n(including the same browser's other tabs).\n\nUse `EventSource` with credentials so the session cookie is sent:\n\n```js\nconst es = new EventSource('https://…/api/events', { withCredentials: true });\nes.addEventListener('category', () => { refetchCategoryTree(); });\nes.addEventListener('articles', () => { refetchArticleIndex(); });\nes.addEventListener('article-42000045', () => { refetchArticle('42000045'); });\n```\n\nKeep-alive comments (`: keepalive`) are sent roughly every 25s.\nReconnect after changing the selected company (the stream is bound\nto the company at connect time).\n\n## Signals\n\n| Signal | Emitted after successful |\n|--------|--------------------------|\n| `category` | `POST /category`, `PATCH /category/{id}`, `PUT /category/order`, `DELETE /category/{id}`, `POST /category/{id}/image`, `DELETE /category/{id}/image` |\n| `articles` | `POST /article`, `PATCH /article/{sku}`, `DELETE /article/{sku}` |\n| `article-<sku>` | `PUT /article/{sku}/weight`, `PUT /article/{sku}/prices`, `PUT /article/{sku}/eans`, `PUT /article/{sku}/categories`, `PUT /article/{sku}/brand`, `PUT /article/{sku}/images/order`, `POST /article/{sku}/image`, `DELETE /article/{sku}/image/{fullHash}` |\n\nSignals are only sent when the mutation actually succeeds (2xx).\nFailed or no-op validation errors do not emit. The `article-<sku>`\nevent name uses the concrete SKU (e.g. `article-42000045`).\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "events.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "SSE stream (`text/event-stream`). Each signal is an SSE event\nwhose name is the signal id and whose data is `{}`.\n",
|
||
"content": {
|
||
"text/event-stream": {
|
||
"schema": {
|
||
"type": "string",
|
||
"example": "event: category\ndata: {}\n\nevent: articles\ndata: {}\n\nevent: article-42000045\ndata: {}\n"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/entity/{entityId}/contact": {
|
||
"get": {
|
||
"tags": [
|
||
"Entity"
|
||
],
|
||
"summary": "List entity contacts",
|
||
"description": "Lists contacts for an entity of the selected company (`contactType` e.g. `email`, `phone`).",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "entity/contact/list.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "entityId",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Contact list",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"contacts": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/EntityContact"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid entityId",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Entity or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"post": {
|
||
"tags": [
|
||
"Entity"
|
||
],
|
||
"summary": "Create entity contact",
|
||
"description": "Creates a contact for the entity. `contactType` and `contact` are required.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "entity/contact/create.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "entityId",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"contactType",
|
||
"contact"
|
||
],
|
||
"properties": {
|
||
"contactType": {
|
||
"type": "string",
|
||
"description": "e.g. email, phone"
|
||
},
|
||
"contact": {
|
||
"type": "string"
|
||
}
|
||
},
|
||
"example": {
|
||
"contactType": "email",
|
||
"contact": "anna@example.com"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"201": {
|
||
"description": "Created contact",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"contact": {
|
||
"$ref": "#/components/schemas/EntityContact"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Contact required, invalid entityId, 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Entity or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/entity/{entityId}/contact/{id}": {
|
||
"patch": {
|
||
"tags": [
|
||
"Entity"
|
||
],
|
||
"summary": "Update entity contact",
|
||
"description": "Patches `contactType` and/or `contact`.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "entity/contact/update.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "entityId",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
},
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"contactType": {
|
||
"type": "string"
|
||
},
|
||
"contact": {
|
||
"type": "string"
|
||
}
|
||
},
|
||
"example": {
|
||
"contact": "anna.mueller@example.com"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated contact",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"contact": {
|
||
"$ref": "#/components/schemas/EntityContact"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Contact required, invalid ids, 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Contact, entity, or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"delete": {
|
||
"tags": [
|
||
"Entity"
|
||
],
|
||
"summary": "Delete entity contact",
|
||
"description": "Deletes one contact of the entity.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "entity/contact/delete.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "entityId",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
},
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"204": {
|
||
"description": "Deleted"
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid ids",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Contact or entity not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/entity/{entityId}/address": {
|
||
"get": {
|
||
"tags": [
|
||
"Entity"
|
||
],
|
||
"summary": "List entity addresses",
|
||
"description": "Lists addresses for an entity of the selected company.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "entity/address/list.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "entityId",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Address list",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"addresses": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/EntityAddress"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid entityId",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Entity or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"post": {
|
||
"tags": [
|
||
"Entity"
|
||
],
|
||
"summary": "Create entity address",
|
||
"description": "Creates an address for the entity. `addressType` is required.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "entity/address/create.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "entityId",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"addressType"
|
||
],
|
||
"properties": {
|
||
"addressType": {
|
||
"type": "string"
|
||
},
|
||
"firstName": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"name": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"street": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"streetNumber": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"zip": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"city": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"county": {
|
||
"type": "string",
|
||
"nullable": true
|
||
}
|
||
},
|
||
"example": {
|
||
"addressType": "billing",
|
||
"street": "Hauptstraße",
|
||
"streetNumber": "12",
|
||
"zip": "10115",
|
||
"city": "Berlin"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"201": {
|
||
"description": "Created address",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"address": {
|
||
"$ref": "#/components/schemas/EntityAddress"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Address type required, invalid entityId, 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Entity or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/entity/{entityId}/address/{id}": {
|
||
"patch": {
|
||
"tags": [
|
||
"Entity"
|
||
],
|
||
"summary": "Update entity address",
|
||
"description": "Patches address fields that are present.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "entity/address/update.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "entityId",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
},
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"addressType": {
|
||
"type": "string"
|
||
},
|
||
"firstName": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"name": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"street": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"streetNumber": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"zip": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"city": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"county": {
|
||
"type": "string",
|
||
"nullable": true
|
||
}
|
||
},
|
||
"example": {
|
||
"city": "München",
|
||
"zip": "80331"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated address",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"address": {
|
||
"$ref": "#/components/schemas/EntityAddress"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Address type required, invalid ids, 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Address, entity, or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"delete": {
|
||
"tags": [
|
||
"Entity"
|
||
],
|
||
"summary": "Delete entity address",
|
||
"description": "Deletes one address of the entity.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "entity/address/delete.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "entityId",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
},
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"204": {
|
||
"description": "Deleted"
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid ids",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Address or entity not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/entity": {
|
||
"get": {
|
||
"tags": [
|
||
"Entity"
|
||
],
|
||
"summary": "List entities",
|
||
"description": "Lists entities for the selected company. Optional `customergroupId`\nfilters to one group. Optional `role=customer` excludes Lieferanten/POS\ngroups (for quote/order pickers). `role` wins when both are set.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "entity/list.md",
|
||
"parameters": [
|
||
{
|
||
"in": "query",
|
||
"name": "customergroupId",
|
||
"required": false,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
},
|
||
{
|
||
"in": "query",
|
||
"name": "role",
|
||
"required": false,
|
||
"schema": {
|
||
"type": "string",
|
||
"enum": [
|
||
"customer"
|
||
]
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Entity list",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"entities": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/Entity"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected, invalid customergroupId, or invalid role",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company or customer group not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"post": {
|
||
"tags": [
|
||
"Entity"
|
||
],
|
||
"summary": "Create entity",
|
||
"description": "Creates an entity in a company customer group and writes `entityhistory` (`Erstellt`).\nOptional `number`: omit to allocate the next customer number (`type` entity).\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "entity/create.md",
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"customergroupId",
|
||
"name"
|
||
],
|
||
"properties": {
|
||
"customergroupId": {
|
||
"type": "integer"
|
||
},
|
||
"name": {
|
||
"type": "string"
|
||
},
|
||
"firstName": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"number": {
|
||
"type": "string",
|
||
"description": "Customer number; omit to auto-allocate"
|
||
}
|
||
},
|
||
"example": {
|
||
"customergroupId": 1,
|
||
"name": "Müller",
|
||
"firstName": "Anna",
|
||
"number": "10042"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"201": {
|
||
"description": "Created entity",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"entity": {
|
||
"$ref": "#/components/schemas/Entity"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Name/number 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company or customer group not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"409": {
|
||
"description": "Number already in use",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/entity/next-number": {
|
||
"get": {
|
||
"tags": [
|
||
"Entity"
|
||
],
|
||
"summary": "Peek next customer number",
|
||
"description": "Returns the next customer number that would be allocated (`type` entity)\nwithout consuming it. Use on the customer add form.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "entity/next-number.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "Next number",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"number"
|
||
],
|
||
"properties": {
|
||
"number": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"example": {
|
||
"number": "1"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/entity/{id}": {
|
||
"get": {
|
||
"tags": [
|
||
"Entity"
|
||
],
|
||
"summary": "Get entity",
|
||
"description": "Returns one entity for the selected company.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "entity/get.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Entity",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"entity": {
|
||
"$ref": "#/components/schemas/Entity"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid id",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Entity or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"patch": {
|
||
"tags": [
|
||
"Entity"
|
||
],
|
||
"summary": "Update entity",
|
||
"description": "Patches fields that are present (`name`, `firstName`, `number`).\n`number` is only changed when the key is present (no auto-allocate).\nWrites `entityhistory` when values change.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "entity/update.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"name": {
|
||
"type": "string"
|
||
},
|
||
"firstName": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"number": {
|
||
"type": "string",
|
||
"nullable": true
|
||
}
|
||
},
|
||
"example": {
|
||
"name": "Müller",
|
||
"firstName": "Anna",
|
||
"number": "10042"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated entity",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"entity": {
|
||
"$ref": "#/components/schemas/Entity"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Name/number required, invalid id, 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Entity or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"409": {
|
||
"description": "Number already in use",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"delete": {
|
||
"tags": [
|
||
"Entity"
|
||
],
|
||
"summary": "Delete entity",
|
||
"description": "Deletes the entity (`entityhistory` cascades; related addresses/contacts cascade).",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "entity/delete.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"204": {
|
||
"description": "Deleted"
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid id",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Entity not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/delivery/parsePDF": {
|
||
"post": {
|
||
"tags": [
|
||
"Delivery"
|
||
],
|
||
"summary": "Parse PDF (SSE)",
|
||
"description": "Accepts a PDF invoice or delivery note (`multipart/form-data`, field `pdf`)\nand streams extraction results as Server-Sent Events until parsing is complete.\n\nThe PDF is read via `pdftotext -layout` and fed page-by-page to an LLM\n(OpenRouter) which fills a structured manifest of header data and line items.\nThe OpenRouter API key is read from the authenticated user's company setting\n`keys.openrouter`; the endpoint returns `400` if the key is not configured.\n\nThe stream ends after the final `result` event (or an `error` event on failure).\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "delivery/parsePDF.md",
|
||
"x-sse-events": [
|
||
{
|
||
"name": "status",
|
||
"description": "PDF parse confirmation and LLM status messages",
|
||
"payload": "{ \"message\": \"…\" }"
|
||
},
|
||
{
|
||
"name": "llm_call",
|
||
"description": "Fired before each LLM request",
|
||
"payload": "{ \"model\": \"…\", \"promptSize\": 0, \"messages\": 0 }"
|
||
},
|
||
{
|
||
"name": "tool_call",
|
||
"description": "Fired when the output_manifest tool is invoked",
|
||
"payload": "{ \"name\": \"output_manifest\" }"
|
||
},
|
||
{
|
||
"name": "usage",
|
||
"description": "Token usage after each LLM response",
|
||
"payload": "{ \"input\": 0, \"cached\": 0, \"output\": 0, \"cost\": 0 }"
|
||
},
|
||
{
|
||
"name": "progress",
|
||
"description": "Intermediate accumulated extraction state (basedata / notes / items)",
|
||
"payload": "ManifestResult"
|
||
},
|
||
{
|
||
"name": "result",
|
||
"description": "Final accumulated extraction — emitted just before the stream closes",
|
||
"payload": "ManifestResult"
|
||
},
|
||
{
|
||
"name": "error",
|
||
"description": "Emitted on PDF parse or LLM failure; stream closes immediately after",
|
||
"payload": "{ \"message\": \"…\" }"
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"multipart/form-data": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"pdf"
|
||
],
|
||
"properties": {
|
||
"pdf": {
|
||
"type": "string",
|
||
"format": "binary",
|
||
"description": "PDF file to parse (max 50 MB, application/pdf)"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "`text/event-stream` — emits `status`, `llm_call`, `tool_call`, `usage`,\n`progress`, `result` (and `error` on failure). See SSE Events table below.\n\n**ManifestResult shape** (used by `progress` and `result`):\n\n```json\n{\n \"basedata\": {\n \"date\": \"2026-01-15\",\n \"kLieferant\": 42,\n \"lieferantName\": \"Muster GmbH\",\n \"manifestNr\": \"LS-2026-001\",\n \"manifestType\": \"lieferschein\"\n },\n \"notes\": [\"Pos. 3: VPE-Angabe fehlte, Standardwert 1 angenommen.\"],\n \"items\": {\n \"pagesFrom\": 1,\n \"pagesTo\": 2,\n \"items\": [\n {\n \"position\": 1,\n \"count\": 10,\n \"vpe\": 1,\n \"unit\": \"Stk.\",\n \"articleName\": \"Einhängesieb Stahl\",\n \"liefArtNr\": \"ES-100\",\n \"mwst\": \"19\",\n \"eknetto\": 450,\n \"page\": 1\n }\n ]\n }\n}\n```\n",
|
||
"content": {
|
||
"text/event-stream": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No PDF uploaded or OpenRouter key not configured",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/delivery": {
|
||
"get": {
|
||
"tags": [
|
||
"Delivery"
|
||
],
|
||
"summary": "List deliveries",
|
||
"description": "Lists goods receipts (`manifest` rows with `type = 'delivery'`)\nfor the authenticated user's selected company.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "delivery/list.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "Delivery list",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"deliveries": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/DeliverySummary"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"post": {
|
||
"tags": [
|
||
"Delivery"
|
||
],
|
||
"summary": "Create delivery",
|
||
"description": "Creates a goods receipt with header and lines in one transaction.\nThe manifest `number` is allocated automatically via\n`nextNumber('delivery')`; the user-supplied reference is stored as\n`externalNumber`. Each line requires `sku` or `name`, `amount`, `net`, and\n`tax` (rate). `pu` defaults to `1`; `rebate` defaults to `0`.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "delivery/create.md",
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"externalNumber",
|
||
"date",
|
||
"lines"
|
||
],
|
||
"properties": {
|
||
"externalNumber": {
|
||
"type": "string",
|
||
"description": "User-supplied reference number"
|
||
},
|
||
"date": {
|
||
"type": "string",
|
||
"format": "date-time"
|
||
},
|
||
"supplierId": {
|
||
"type": "integer",
|
||
"nullable": true
|
||
},
|
||
"lines": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/DeliveryLineInput"
|
||
}
|
||
}
|
||
},
|
||
"example": {
|
||
"externalNumber": "DHL-123",
|
||
"date": "2026-08-10",
|
||
"supplierId": 1,
|
||
"lines": [
|
||
{
|
||
"sku": "1",
|
||
"name": "Sample Article",
|
||
"pu": 1,
|
||
"amount": 10,
|
||
"net": 99.5,
|
||
"tax": 19
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"201": {
|
||
"description": "Created delivery",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"delivery": {
|
||
"$ref": "#/components/schemas/Delivery"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid delivery, invalid lines, 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company, supplier, or article not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/delivery/{id}": {
|
||
"get": {
|
||
"tags": [
|
||
"Delivery"
|
||
],
|
||
"summary": "Get delivery",
|
||
"description": "Returns a goods receipt with lines for the selected company.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "delivery/get.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Delivery",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"delivery": {
|
||
"$ref": "#/components/schemas/Delivery"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid id",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Delivery or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"patch": {
|
||
"tags": [
|
||
"Delivery"
|
||
],
|
||
"summary": "Update delivery",
|
||
"description": "Patches header fields that are present. The manifest `number` is\nauto-allocated and cannot be patched; only the user-supplied\n`externalNumber` reference is editable. If `lines` is present,\nreplaces all lines; if omitted, leaves lines unchanged.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "delivery/update.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"externalNumber": {
|
||
"type": "string",
|
||
"nullable": true,
|
||
"description": "User-supplied reference number (empty/null clears it)"
|
||
},
|
||
"date": {
|
||
"type": "string",
|
||
"format": "date-time"
|
||
},
|
||
"supplierId": {
|
||
"type": "integer",
|
||
"nullable": true
|
||
},
|
||
"lines": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/DeliveryLineInput"
|
||
}
|
||
}
|
||
},
|
||
"example": {
|
||
"externalNumber": "DHL-124",
|
||
"lines": []
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated delivery",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"delivery": {
|
||
"$ref": "#/components/schemas/Delivery"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid delivery, invalid lines, invalid id, 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Delivery, supplier, article, or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"delete": {
|
||
"tags": [
|
||
"Delivery"
|
||
],
|
||
"summary": "Delete delivery",
|
||
"description": "Deletes the goods receipt (`manifestarticle` rows cascade).\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "delivery/delete.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"204": {
|
||
"description": "Deleted"
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid id",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Delivery not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/customergroup": {
|
||
"get": {
|
||
"tags": [
|
||
"Customergroup"
|
||
],
|
||
"summary": "List customer groups",
|
||
"description": "Lists customer groups for the selected company, ordered by `sort`.\nEach item includes `editable`: `false` for implicit `POS` / `Lieferanten`\n(name locked), `true` otherwise. Reorder via `PUT /customergroup/order`.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "customergroup/list.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "Customer group list",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"customergroups": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/CustomergroupListItem"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"post": {
|
||
"tags": [
|
||
"Customergroup"
|
||
],
|
||
"summary": "Create customer group",
|
||
"description": "Creates a customer group for the selected company.\nAppends with the next `sort` index. Reserved names `POS` and `Lieferanten`\nare not allowed (`Name not allowed`).\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "customergroup/create.md",
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"name"
|
||
],
|
||
"properties": {
|
||
"name": {
|
||
"type": "string"
|
||
}
|
||
},
|
||
"example": {
|
||
"name": "Kunden"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"201": {
|
||
"description": "Created customer group",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"customergroup": {
|
||
"$ref": "#/components/schemas/Customergroup"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Name required, name not allowed, 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/customergroup/order": {
|
||
"put": {
|
||
"tags": [
|
||
"Customergroup"
|
||
],
|
||
"summary": "Reorder customer groups",
|
||
"description": "Sets `sort` to each id’s index `0…n-1` in one transaction.\n`customergroupIds` must be exactly the company’s groups (same set, no dups)\nelse `Invalid customer group order`.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "customergroup/order.md",
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"customergroupIds"
|
||
],
|
||
"properties": {
|
||
"customergroupIds": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
},
|
||
"example": {
|
||
"customergroupIds": [
|
||
4,
|
||
3,
|
||
1,
|
||
2
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "New order",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"customergroupIds": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid customer group order 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/customergroup/{id}": {
|
||
"patch": {
|
||
"tags": [
|
||
"Customergroup"
|
||
],
|
||
"summary": "Update customer group",
|
||
"description": "Renames a customer group. Implicit `POS` / `Lieferanten` cannot be\nrenamed (`Name cannot be changed`). Reorder via `PUT /customergroup/order`.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "customergroup/update.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"name"
|
||
],
|
||
"properties": {
|
||
"name": {
|
||
"type": "string"
|
||
}
|
||
},
|
||
"example": {
|
||
"name": "Stammkunden"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated customer group",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"customergroup": {
|
||
"$ref": "#/components/schemas/Customergroup"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Name required, name cannot be changed, invalid id, 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Customer group or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"delete": {
|
||
"tags": [
|
||
"Customergroup"
|
||
],
|
||
"summary": "Delete customer group",
|
||
"description": "Deletes the customer group (cascades entities). Fails with\n`Customer group in use` for the POS group or other NO ACTION references.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "customergroup/delete.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"204": {
|
||
"description": "Deleted"
|
||
},
|
||
"400": {
|
||
"description": "No company selected or invalid id",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Customer group not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"409": {
|
||
"description": "Customer group in use",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/company/setting": {
|
||
"get": {
|
||
"tags": [
|
||
"CompanySetting"
|
||
],
|
||
"summary": "List company settings",
|
||
"description": "Returns the visible settings of the authenticated user's selected\ncompany: the full set of allowed keys (with `value: null` when unset)\nplus any stored settings that are frontend-visible. The `frontend`\nflag is stripped from responses.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "companysetting/list.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "Company settings",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"settings": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/CompanySetting"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/company/setting/{key}": {
|
||
"get": {
|
||
"tags": [
|
||
"CompanySetting"
|
||
],
|
||
"summary": "Get company setting by key",
|
||
"description": "Returns a single company setting for the authenticated user's selected\ncompany, or `404` if it does not exist.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "companysetting/get.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "key",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Company setting",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"setting": {
|
||
"$ref": "#/components/schemas/CompanySetting"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company setting or company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"put": {
|
||
"tags": [
|
||
"CompanySetting"
|
||
],
|
||
"summary": "Upsert company setting",
|
||
"description": "Sets (upserts) a company setting for the selected company.\nOnly a fixed allow-list of keys can be set: `ui.theme`, `keys.tavily`,\n`keys.openrouter`, `labelprinter.addr`, `labelprinter.type`.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "companysetting/set.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "key",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"value"
|
||
],
|
||
"properties": {
|
||
"value": {
|
||
"type": "string"
|
||
},
|
||
"frontend": {
|
||
"type": "boolean"
|
||
}
|
||
},
|
||
"example": {
|
||
"value": "dark",
|
||
"frontend": true
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Upserted company setting",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"setting": {
|
||
"$ref": "#/components/schemas/CompanySetting"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid setting, invalid key, 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/company": {
|
||
"get": {
|
||
"tags": [
|
||
"Company"
|
||
],
|
||
"summary": "List companies",
|
||
"description": "Lists companies for the authenticated user.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "company/list.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "Company list",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"companies": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/Company"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"500": {
|
||
"description": "Server error",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"post": {
|
||
"tags": [
|
||
"Company"
|
||
],
|
||
"summary": "Create company",
|
||
"description": "Creates a company and selects it for the current user.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "company/create.md",
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"iso639",
|
||
"name",
|
||
"taxrate",
|
||
"reducedtaxrate"
|
||
],
|
||
"properties": {
|
||
"iso639": {
|
||
"type": "string"
|
||
},
|
||
"name": {
|
||
"type": "string"
|
||
},
|
||
"taxrate": {
|
||
"type": "number"
|
||
},
|
||
"reducedtaxrate": {
|
||
"type": "number"
|
||
}
|
||
},
|
||
"example": {
|
||
"iso639": "de",
|
||
"name": "Acme GmbH",
|
||
"taxrate": 19,
|
||
"reducedtaxrate": 7
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"201": {
|
||
"description": "Created; selected company updated",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"company": {
|
||
"$ref": "#/components/schemas/Company"
|
||
},
|
||
"user": {
|
||
"$ref": "#/components/schemas/User"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Validation error",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"409": {
|
||
"description": "Company name not available",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/company/{id}": {
|
||
"put": {
|
||
"tags": [
|
||
"Company"
|
||
],
|
||
"summary": "Update company",
|
||
"description": "Updates a company.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "company/update.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"iso639",
|
||
"name",
|
||
"taxrate",
|
||
"reducedtaxrate"
|
||
],
|
||
"properties": {
|
||
"iso639": {
|
||
"type": "string"
|
||
},
|
||
"name": {
|
||
"type": "string"
|
||
},
|
||
"taxrate": {
|
||
"type": "number"
|
||
},
|
||
"reducedtaxrate": {
|
||
"type": "number"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"company": {
|
||
"$ref": "#/components/schemas/Company"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Validation error",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"409": {
|
||
"description": "Company name not available",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"delete": {
|
||
"tags": [
|
||
"Company"
|
||
],
|
||
"summary": "Delete company",
|
||
"description": "Deletes a company.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "company/delete.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"204": {
|
||
"description": "Deleted"
|
||
},
|
||
"400": {
|
||
"description": "Invalid id",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"500": {
|
||
"description": "Server error",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/category/tree": {
|
||
"get": {
|
||
"tags": [
|
||
"Category"
|
||
],
|
||
"summary": "Category tree",
|
||
"description": "Returns the category tree for the authenticated user's selected company.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "category/tree.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "Tree and articles for the selected company",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"languageId": {
|
||
"type": "integer"
|
||
},
|
||
"tree": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "object",
|
||
"additionalProperties": true
|
||
}
|
||
},
|
||
"articles": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "object",
|
||
"additionalProperties": true
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/category/export.zip": {
|
||
"get": {
|
||
"tags": [
|
||
"Category"
|
||
],
|
||
"summary": "Export categories as ZIP",
|
||
"description": "Builds a `kategorien.zip` for the authenticated user's selected company.\nThe ZIP contains `kategorien.csv` (semicolon-separated, Windows-1252,\ndepth-first in sort order with an `Ebene N` name column per tree depth)\nplus one AVIF entry per category image, named `<fullHash>.avif`\n(content-addressed, so shared hashes are deduplicated).\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "ZIP archive",
|
||
"content": {
|
||
"application/zip": {
|
||
"schema": {
|
||
"type": "string",
|
||
"format": "binary"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/category": {
|
||
"post": {
|
||
"tags": [
|
||
"Category"
|
||
],
|
||
"summary": "Create category",
|
||
"description": "Creates a category under an authorized parent.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"category"
|
||
],
|
||
"x-markdown": "category/create.md",
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"parentId",
|
||
"name"
|
||
],
|
||
"properties": {
|
||
"parentId": {
|
||
"type": "integer"
|
||
},
|
||
"name": {
|
||
"type": "string"
|
||
}
|
||
},
|
||
"example": {
|
||
"parentId": 1,
|
||
"name": "New category"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"201": {
|
||
"description": "Created",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"category": {
|
||
"$ref": "#/components/schemas/Category"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Validation error",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Parent not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/category/{id}/articles": {
|
||
"get": {
|
||
"tags": [
|
||
"Category"
|
||
],
|
||
"summary": "Articles in category",
|
||
"description": "Returns articles in a category the user is authorized for.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "category/articles.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
},
|
||
"description": "Category id"
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Articles list",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"articles": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "object",
|
||
"additionalProperties": true
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid id",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/category/{id}": {
|
||
"patch": {
|
||
"tags": [
|
||
"Category"
|
||
],
|
||
"summary": "Update category (rename or move)",
|
||
"description": "Updates a category the user is authorized for.\n`name` renames the category (re-slugifies in the company default language).\n`parentId` moves it under a new parent (cycle/self → `Invalid parent`).\nEither `name` or `parentId` (or both) is required.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"category"
|
||
],
|
||
"x-markdown": "category/update.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
},
|
||
"description": "Category id"
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"name": {
|
||
"type": "string"
|
||
},
|
||
"parentId": {
|
||
"type": "integer",
|
||
"description": "New parent category id"
|
||
}
|
||
},
|
||
"example": {
|
||
"parentId": 3
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated category (renamed and/or moved)",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"category": {
|
||
"type": "object",
|
||
"required": [
|
||
"id",
|
||
"name",
|
||
"slug",
|
||
"parentId",
|
||
"sort"
|
||
],
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"name": {
|
||
"type": "string"
|
||
},
|
||
"slug": {
|
||
"type": "string"
|
||
},
|
||
"parentId": {
|
||
"type": "integer"
|
||
},
|
||
"sort": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Validation error (name or parentId required, or invalid parent)",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Category not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"delete": {
|
||
"tags": [
|
||
"Category"
|
||
],
|
||
"summary": "Delete category",
|
||
"description": "Deletes a category the user is authorized for (not the company root).",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"category"
|
||
],
|
||
"x-markdown": "category/delete.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
},
|
||
"description": "Category id"
|
||
}
|
||
],
|
||
"responses": {
|
||
"204": {
|
||
"description": "Deleted"
|
||
},
|
||
"400": {
|
||
"description": "Invalid id or cannot delete root",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/category/order": {
|
||
"put": {
|
||
"tags": [
|
||
"Category"
|
||
],
|
||
"summary": "Reorder sibling categories",
|
||
"description": "Sets the sibling sort order of `categoryIds` under `parentId`.\n`categoryIds` must be exactly the current child set of `parentId`\n(same count, no foreign/duplicate ids). Each child's `sort` is set\nto its index `0..n-1` in one transaction.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"category"
|
||
],
|
||
"x-markdown": "category/order.md",
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"parentId",
|
||
"categoryIds"
|
||
],
|
||
"properties": {
|
||
"parentId": {
|
||
"type": "integer",
|
||
"description": "Parent category under which sibling order applies"
|
||
},
|
||
"categoryIds": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
},
|
||
"example": {
|
||
"parentId": 1,
|
||
"categoryIds": [
|
||
2,
|
||
4,
|
||
3,
|
||
5
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "New sibling order",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"categoryIds": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid category order",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Parent category not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/category/{id}/image": {
|
||
"post": {
|
||
"tags": [
|
||
"Category"
|
||
],
|
||
"summary": "Add category image",
|
||
"description": "Accepts a full-size AVIF body (`Content-Type: image/avif`).\nFull: stored as-is when already AVIF and ≤1000×1000; otherwise scaled to max 1000×1000.\nThumb always scaled to max 200×200. Stores hash/thumbhash, links `categorypic`.\nAuth like GET /category/tree — user must be authorized for the category's company.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"category"
|
||
],
|
||
"x-markdown": "category/add-image.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
},
|
||
"description": "Category id"
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"image/avif": {
|
||
"schema": {
|
||
"type": "string",
|
||
"format": "binary"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"201": {
|
||
"description": "Image stored and linked",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"category": {
|
||
"type": "object",
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"thumbW": {
|
||
"type": "integer"
|
||
},
|
||
"thumbH": {
|
||
"type": "integer"
|
||
},
|
||
"thumbHash": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Missing body or invalid image",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Category not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"delete": {
|
||
"tags": [
|
||
"Category"
|
||
],
|
||
"summary": "Delete category image",
|
||
"description": "Removes the category's image. Sets thumbW, thumbH, thumbHash back to null.\nAuth like GET /category/tree — user must be authorized for the category's company.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"category"
|
||
],
|
||
"x-markdown": "category/delete-image.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
},
|
||
"description": "Category id"
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Image removed",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"category": {
|
||
"type": "object",
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"thumbW": {
|
||
"type": "integer",
|
||
"nullable": true
|
||
},
|
||
"thumbH": {
|
||
"type": "integer",
|
||
"nullable": true
|
||
},
|
||
"thumbHash": {
|
||
"type": "string",
|
||
"nullable": true
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Category or image not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/brand": {
|
||
"get": {
|
||
"tags": [
|
||
"Brand"
|
||
],
|
||
"summary": "List brands",
|
||
"description": "Lists brands for the authenticated user's selected company, sorted by name.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "brand/list.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "Brand list",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"brands": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/Brand"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"put": {
|
||
"tags": [
|
||
"Brand"
|
||
],
|
||
"summary": "Upsert brand",
|
||
"description": "Insert or update brand by `name` for the selected company. `gsprpinfo` optional.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "brand/upsert.md",
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"name"
|
||
],
|
||
"properties": {
|
||
"name": {
|
||
"type": "string"
|
||
},
|
||
"gsprpinfo": {
|
||
"type": "string"
|
||
}
|
||
},
|
||
"example": {
|
||
"name": "Acme",
|
||
"gsprpinfo": "Acme GmbH"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Upserted brand",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"brand": {
|
||
"$ref": "#/components/schemas/Brand"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid brand 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": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/brand/{id}": {
|
||
"delete": {
|
||
"tags": [
|
||
"Brand"
|
||
],
|
||
"summary": "Delete brand",
|
||
"description": "Deletes the brand for the selected company. Fails with `Brand in use`\nif any article still references it.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "brand/delete.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "id",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"204": {
|
||
"description": "Deleted"
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Brand not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"409": {
|
||
"description": "Brand in use",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/article": {
|
||
"post": {
|
||
"tags": [
|
||
"Article"
|
||
],
|
||
"summary": "Create article",
|
||
"description": "Creates an article under an authorized category (`parentId`).",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"articles"
|
||
],
|
||
"x-markdown": "article/create.md",
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"parentId",
|
||
"name"
|
||
],
|
||
"properties": {
|
||
"parentId": {
|
||
"type": "integer",
|
||
"description": "Category id"
|
||
},
|
||
"name": {
|
||
"type": "string"
|
||
}
|
||
},
|
||
"example": {
|
||
"parentId": 10,
|
||
"name": "New article"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"201": {
|
||
"description": "Created",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"article": {
|
||
"$ref": "#/components/schemas/ArticleSummary"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Validation error",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized for parent category",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Parent not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/article/export.zip": {
|
||
"get": {
|
||
"tags": [
|
||
"Article"
|
||
],
|
||
"summary": "Export articles as ZIP",
|
||
"description": "Builds an `artikel.zip` for the authenticated user's selected company.\n`categoryId` (optional) restricts the export to that category and below;\nit must be reachable via the company's root category. Defaults to the\nwhole company root tree.\nThe ZIP contains `artikel.csv` (semicolon-separated, Windows-1252)\nplus one AVIF entry per article image, named `<fullHash>.avif`\n(content-addressed, so shared hashes are deduplicated).\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"parameters": [
|
||
{
|
||
"in": "query",
|
||
"name": "categoryId",
|
||
"required": false,
|
||
"schema": {
|
||
"type": "integer"
|
||
},
|
||
"description": "Restrict export to this category subtree"
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "ZIP archive",
|
||
"content": {
|
||
"application/zip": {
|
||
"schema": {
|
||
"type": "string",
|
||
"format": "binary"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/article/{sku}": {
|
||
"get": {
|
||
"tags": [
|
||
"Article"
|
||
],
|
||
"summary": "Get article by SKU",
|
||
"description": "Returns article detail by SKU if the user may access its category.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "article/get.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "sku",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string"
|
||
},
|
||
"description": "Article SKU"
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Article detail",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"article": {
|
||
"$ref": "#/components/schemas/ArticleDetail"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Missing sku",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Article not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"patch": {
|
||
"tags": [
|
||
"Article"
|
||
],
|
||
"summary": "Update article by SKU",
|
||
"description": "Patches article fields. At least one of `name`, `unitamount`, `unitSi` required.\n`unitSi` must already exist for the company. Returns the same detail shape as GET.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"articles"
|
||
],
|
||
"x-markdown": "article/update.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "sku",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string"
|
||
},
|
||
"description": "Article SKU"
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"name": {
|
||
"type": "string"
|
||
},
|
||
"unitamount": {
|
||
"type": "number",
|
||
"description": "Must be finite and `> 0`"
|
||
},
|
||
"unitSi": {
|
||
"type": "string",
|
||
"description": "Existing company unit `si`"
|
||
}
|
||
},
|
||
"example": {
|
||
"unitamount": 0.24,
|
||
"unitSi": "l"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated article detail",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"article": {
|
||
"$ref": "#/components/schemas/ArticleDetail"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Validation error (Name required / Invalid unitamount)",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Article or unit not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"delete": {
|
||
"tags": [
|
||
"Article"
|
||
],
|
||
"summary": "Delete article by SKU",
|
||
"description": "Deletes the article when it has no child articles. Cascades related rows;\nnulls `manifestarticle.article_id` and appends a delete comment.\nAuth like GET by SKU.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"articles"
|
||
],
|
||
"x-markdown": "article/delete.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "sku",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string"
|
||
},
|
||
"description": "Article SKU"
|
||
}
|
||
],
|
||
"responses": {
|
||
"204": {
|
||
"description": "Deleted"
|
||
},
|
||
"400": {
|
||
"description": "Article has children",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Article not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/article/{sku}/history": {
|
||
"get": {
|
||
"tags": [
|
||
"Article"
|
||
],
|
||
"summary": "Get article history by SKU",
|
||
"description": "Returns `articlehistory` rows for the article, newest first (`date` DESC, `id` DESC).\nAuth like GET by SKU.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "article/history.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "sku",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string"
|
||
},
|
||
"description": "Article SKU"
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Article history (newest first)",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"history"
|
||
],
|
||
"properties": {
|
||
"history": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/ArticleHistoryEntry"
|
||
}
|
||
}
|
||
},
|
||
"example": {
|
||
"history": [
|
||
{
|
||
"id": 42,
|
||
"date": "2026-08-05T07:15:00.000Z",
|
||
"text": "Name geändert von \"Tee\" auf \"Grüner Tee\"\nEinheit geändert von \"500g\" auf \"1kg\""
|
||
},
|
||
{
|
||
"id": 41,
|
||
"date": "2026-08-01T09:00:00.000Z",
|
||
"text": "Name geändert von \"\" auf \"Tee\""
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Missing sku",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Article not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/article/{sku}/prices": {
|
||
"put": {
|
||
"tags": [
|
||
"Article"
|
||
],
|
||
"summary": "Replace POS prices by SKU",
|
||
"description": "Replace-all prices for the company POS customer group.\nEach entry: `price` (net), `tax` (rate, ensured via tax.ensure), optional `minmultiplier` (default 1).\nEmpty `prices` clears all POS prices. Returns the same shape as GET by SKU.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"article-<sku>"
|
||
],
|
||
"x-markdown": "article/set-prices.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "sku",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"prices"
|
||
],
|
||
"properties": {
|
||
"prices": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "object",
|
||
"required": [
|
||
"price",
|
||
"tax"
|
||
],
|
||
"properties": {
|
||
"price": {
|
||
"type": "number",
|
||
"description": "Net price"
|
||
},
|
||
"tax": {
|
||
"type": "number",
|
||
"description": "Tax rate (e.g. 19)"
|
||
},
|
||
"minmultiplier": {
|
||
"type": "number",
|
||
"description": "Defaults to 1; must be unique per request"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"example": {
|
||
"prices": [
|
||
{
|
||
"price": 10.5,
|
||
"tax": 19
|
||
},
|
||
{
|
||
"price": 9.9,
|
||
"tax": 19,
|
||
"minmultiplier": 10
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated article detail",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"article": {
|
||
"$ref": "#/components/schemas/ArticleDetail"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid prices or POS customer group required",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Article not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/article/{sku}/image": {
|
||
"post": {
|
||
"tags": [
|
||
"Article"
|
||
],
|
||
"summary": "Add article image",
|
||
"description": "Accepts a full-size AVIF body (`Content-Type: image/avif`).\nFull: stored as-is when already AVIF and ≤1000×1000; otherwise scaled to max 1000×1000.\nThumb always scaled to max 200×200. Stores hash/thumbhash, links `articlepic`.\nAuth like GET by SKU.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"article-<sku>"
|
||
],
|
||
"x-markdown": "article/add-image.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "sku",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string"
|
||
},
|
||
"description": "Article SKU"
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"image/avif": {
|
||
"schema": {
|
||
"type": "string",
|
||
"format": "binary"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"201": {
|
||
"description": "Image stored and linked",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"image": {
|
||
"$ref": "#/components/schemas/ArticleImage"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Missing body or invalid image",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Article not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/article/{sku}/image/{fullHash}": {
|
||
"delete": {
|
||
"tags": [
|
||
"Article"
|
||
],
|
||
"summary": "Delete article image",
|
||
"description": "Removes `articlepic` and the `pic` row for this article (`fullHash`). Auth like GET by SKU.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"article-<sku>"
|
||
],
|
||
"x-markdown": "article/delete-image.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "sku",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
},
|
||
{
|
||
"in": "path",
|
||
"name": "fullHash",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string",
|
||
"pattern": "^[a-fA-F0-9]{64}$"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"204": {
|
||
"description": "Deleted"
|
||
},
|
||
"400": {
|
||
"description": "Invalid hash",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Article or image not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/article/{sku}/images/order": {
|
||
"put": {
|
||
"tags": [
|
||
"Article"
|
||
],
|
||
"summary": "Reorder article images",
|
||
"description": "Sets `articlepic.sort` to `0..n-1` in the given order.\n`fullHashes` must list every image of the article exactly once.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"article-<sku>"
|
||
],
|
||
"x-markdown": "article/order-images.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "sku",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"fullHashes"
|
||
],
|
||
"properties": {
|
||
"fullHashes": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "string",
|
||
"pattern": "^[a-fA-F0-9]{64}$"
|
||
}
|
||
}
|
||
},
|
||
"example": {
|
||
"fullHashes": [
|
||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "New order",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"fullHashes": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid image order",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Article not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/article/{sku}/eans": {
|
||
"put": {
|
||
"tags": [
|
||
"Article"
|
||
],
|
||
"summary": "Replace article EANs by SKU",
|
||
"description": "Replace-all EANs for the article. EANs must be non-empty unique strings;\nempty array clears all EANs. Returns the same shape as GET by SKU.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"article-<sku>"
|
||
],
|
||
"x-markdown": "article/set-eans.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "sku",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"eans"
|
||
],
|
||
"properties": {
|
||
"eans": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"example": {
|
||
"eans": [
|
||
"4001234567890"
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated article detail",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"article": {
|
||
"$ref": "#/components/schemas/ArticleDetail"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid eans",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Article not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/article/{sku}/brand": {
|
||
"put": {
|
||
"tags": [
|
||
"Article"
|
||
],
|
||
"summary": "Set or clear article brand by SKU",
|
||
"description": "Sets the article's `brandId` (must belong to the article's company).\nPass `null` to clear the brand. Returns the same shape as GET by SKU.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"article-<sku>"
|
||
],
|
||
"x-markdown": "article/set-brand.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "sku",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"brandId"
|
||
],
|
||
"properties": {
|
||
"brandId": {
|
||
"type": "integer",
|
||
"nullable": true
|
||
}
|
||
},
|
||
"example": {
|
||
"brandId": 3
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated article detail",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"article": {
|
||
"$ref": "#/components/schemas/ArticleDetail"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Validation error",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Article or brand not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/article/{sku}/weight": {
|
||
"put": {
|
||
"tags": [
|
||
"Article"
|
||
],
|
||
"summary": "Set article weight by SKU",
|
||
"description": "Sets the article's `weight`. Must be finite and `>= 0`.\nReturns the same shape as GET by SKU.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"article-<sku>"
|
||
],
|
||
"x-markdown": "article/set-weight.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "sku",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"weight"
|
||
],
|
||
"properties": {
|
||
"weight": {
|
||
"type": "number"
|
||
}
|
||
},
|
||
"example": {
|
||
"weight": 1.5
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated article detail",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"article": {
|
||
"$ref": "#/components/schemas/ArticleDetail"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid weight",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Article not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/article/{sku}/categories": {
|
||
"put": {
|
||
"tags": [
|
||
"Article"
|
||
],
|
||
"summary": "Replace article categories by SKU",
|
||
"description": "Replace-all the article's category list. Each category must belong to\nthe article's company; ids must be unique. Empty array removes the\narticle from all categories. Returns the same shape as GET by SKU.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-signals": [
|
||
"article-<sku>"
|
||
],
|
||
"x-markdown": "article/set-categories.md",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "sku",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"categoryIds"
|
||
],
|
||
"properties": {
|
||
"categoryIds": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
},
|
||
"example": {
|
||
"categoryIds": [
|
||
4,
|
||
7
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated article detail",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"article": {
|
||
"$ref": "#/components/schemas/ArticleDetail"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid categoryIds",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Article not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/articleIndex": {
|
||
"get": {
|
||
"tags": [
|
||
"Article"
|
||
],
|
||
"summary": "List article search index",
|
||
"description": "Returns every article reachable via the company's category tree.\nDesigned for client-side search / autocomplete preload.\nAuth via company access.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "Search index",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"articles": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "object",
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"sku": {
|
||
"type": "string"
|
||
},
|
||
"name": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"eans": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "string"
|
||
}
|
||
},
|
||
"categoryIds": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "No company selected",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Not authorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": {
|
||
"description": "Company not found",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/auth/selected-company": {
|
||
"put": {
|
||
"tags": [
|
||
"Auth"
|
||
],
|
||
"summary": "Set selected company",
|
||
"description": "Sets the authenticated user's selected company.",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "auth/selected-company.md",
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"companyId"
|
||
],
|
||
"properties": {
|
||
"companyId": {
|
||
"type": "integer",
|
||
"minimum": 1
|
||
}
|
||
},
|
||
"example": {
|
||
"companyId": 1
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Updated user",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"user": {
|
||
"$ref": "#/components/schemas/User"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Invalid companyId",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"500": {
|
||
"description": "Server error",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/auth/me": {
|
||
"get": {
|
||
"tags": [
|
||
"Auth"
|
||
],
|
||
"summary": "Current user",
|
||
"description": "Returns the authenticated user from the session cookie and the Google client ID.\n`user.picture` is the stored profile image as a base64 string when present.\nUnauthenticated calls still receive `googleClientId` with 401.\n",
|
||
"security": [
|
||
{
|
||
"cookieAuth": []
|
||
}
|
||
],
|
||
"x-markdown": "auth/me.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "Authenticated",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"user": {
|
||
"$ref": "#/components/schemas/User"
|
||
},
|
||
"googleClientId": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Unauthorized — still includes `googleClientId` for the frontend",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"error": {
|
||
"type": "string"
|
||
},
|
||
"googleClientId": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/auth/logout": {
|
||
"post": {
|
||
"tags": [
|
||
"Auth"
|
||
],
|
||
"summary": "Logout",
|
||
"description": "Clears the HTTP-only `token` cookie set at login.",
|
||
"security": [],
|
||
"x-markdown": "auth/logout.md",
|
||
"responses": {
|
||
"200": {
|
||
"description": "Logged out",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"message": {
|
||
"type": "string",
|
||
"example": "Logged out successfully"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/auth/login": {
|
||
"post": {
|
||
"tags": [
|
||
"Auth"
|
||
],
|
||
"summary": "Login with Google ID token",
|
||
"description": "Verifies a Google ID token, checks allowlist (or creates entry when `IMPLICIT_CREATE=true`),\nstores Google `sub` and profile image bytes, and sets an HTTP-only `token` cookie.\n`user.picture` is the profile image as base64 (same as GET /auth/me).\n",
|
||
"security": [],
|
||
"x-markdown": "auth/login.md",
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"required": [
|
||
"idToken"
|
||
],
|
||
"properties": {
|
||
"idToken": {
|
||
"type": "string",
|
||
"description": "Google ID token from Google Identity Services"
|
||
}
|
||
},
|
||
"example": {
|
||
"idToken": "<google-id-token>"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"description": "Login successful; `token` cookie set",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"message": {
|
||
"type": "string",
|
||
"example": "Login successful"
|
||
},
|
||
"user": {
|
||
"$ref": "#/components/schemas/User"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": {
|
||
"description": "Missing token or Google client not configured",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": {
|
||
"description": "Invalid Google token or account",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"403": {
|
||
"description": "Email not on allowlist",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"500": {
|
||
"description": "Server error",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Error"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"components": {
|
||
"securitySchemes": {
|
||
"cookieAuth": {
|
||
"type": "apiKey",
|
||
"in": "cookie",
|
||
"name": "token",
|
||
"description": "HTTP-only session cookie set by POST /auth/login"
|
||
}
|
||
},
|
||
"schemas": {
|
||
"Error": {
|
||
"type": "object",
|
||
"required": [
|
||
"error"
|
||
],
|
||
"properties": {
|
||
"error": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"User": {
|
||
"type": "object",
|
||
"required": [
|
||
"id",
|
||
"email",
|
||
"selectedCompanyId"
|
||
],
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"email": {
|
||
"type": "string",
|
||
"format": "email"
|
||
},
|
||
"selectedCompanyId": {
|
||
"type": "integer",
|
||
"nullable": true
|
||
},
|
||
"picture": {
|
||
"type": "string",
|
||
"description": "Profile image as base64 (from stored picturebinary); omitted when unset"
|
||
}
|
||
}
|
||
},
|
||
"Company": {
|
||
"type": "object",
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"iso639": {
|
||
"type": "string"
|
||
},
|
||
"name": {
|
||
"type": "string"
|
||
},
|
||
"taxrate": {
|
||
"type": "number"
|
||
},
|
||
"reducedtaxrate": {
|
||
"type": "number"
|
||
}
|
||
}
|
||
},
|
||
"CompanySetting": {
|
||
"type": "object",
|
||
"required": [
|
||
"key",
|
||
"value"
|
||
],
|
||
"properties": {
|
||
"key": {
|
||
"type": "string"
|
||
},
|
||
"value": {
|
||
"type": "string",
|
||
"nullable": true
|
||
}
|
||
}
|
||
},
|
||
"Category": {
|
||
"type": "object",
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"parentId": {
|
||
"type": "integer",
|
||
"nullable": true
|
||
},
|
||
"sort": {
|
||
"type": "integer"
|
||
},
|
||
"name": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"Unit": {
|
||
"type": "object",
|
||
"required": [
|
||
"si",
|
||
"basesi",
|
||
"basesifactor"
|
||
],
|
||
"properties": {
|
||
"si": {
|
||
"type": "string"
|
||
},
|
||
"basesi": {
|
||
"type": "string"
|
||
},
|
||
"basesifactor": {
|
||
"type": "number"
|
||
},
|
||
"symbol": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"name": {
|
||
"type": "string",
|
||
"nullable": true
|
||
}
|
||
}
|
||
},
|
||
"ArticleSummary": {
|
||
"type": "object",
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"parentId": {
|
||
"type": "integer"
|
||
},
|
||
"name": {
|
||
"type": "string"
|
||
},
|
||
"sku": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"ArticleDetail": {
|
||
"type": "object",
|
||
"description": "Full article payload (unit, brand, categoryIds, photos, eans, prices, …)",
|
||
"additionalProperties": true
|
||
},
|
||
"ArticleHistoryEntry": {
|
||
"type": "object",
|
||
"required": [
|
||
"id",
|
||
"date",
|
||
"text"
|
||
],
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"date": {
|
||
"type": "string",
|
||
"format": "date-time"
|
||
},
|
||
"text": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"Brand": {
|
||
"type": "object",
|
||
"required": [
|
||
"id",
|
||
"name"
|
||
],
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"name": {
|
||
"type": "string"
|
||
},
|
||
"gsprpinfo": {
|
||
"type": "string",
|
||
"nullable": true
|
||
}
|
||
}
|
||
},
|
||
"NumberSequence": {
|
||
"type": "object",
|
||
"required": [
|
||
"id",
|
||
"type",
|
||
"prefix",
|
||
"padding",
|
||
"next"
|
||
],
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"type": {
|
||
"type": "string",
|
||
"description": "Sequence type (e.g. entity, article, delivery)"
|
||
},
|
||
"prefix": {
|
||
"type": "string"
|
||
},
|
||
"padding": {
|
||
"type": "integer",
|
||
"description": "Zero-padding length for the numeric part"
|
||
},
|
||
"next": {
|
||
"type": "integer",
|
||
"description": "Next value to allocate"
|
||
}
|
||
}
|
||
},
|
||
"Supplier": {
|
||
"type": "object",
|
||
"required": [
|
||
"id",
|
||
"name",
|
||
"createdAt",
|
||
"updatedAt"
|
||
],
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"name": {
|
||
"type": "string"
|
||
},
|
||
"createdAt": {
|
||
"type": "string",
|
||
"format": "date-time"
|
||
},
|
||
"updatedAt": {
|
||
"type": "string",
|
||
"format": "date-time"
|
||
}
|
||
}
|
||
},
|
||
"Customergroup": {
|
||
"type": "object",
|
||
"required": [
|
||
"id",
|
||
"name",
|
||
"sort"
|
||
],
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"name": {
|
||
"type": "string"
|
||
},
|
||
"sort": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
},
|
||
"CustomergroupListItem": {
|
||
"allOf": [
|
||
{
|
||
"$ref": "#/components/schemas/Customergroup"
|
||
},
|
||
{
|
||
"type": "object",
|
||
"required": [
|
||
"editable"
|
||
],
|
||
"properties": {
|
||
"editable": {
|
||
"type": "boolean",
|
||
"description": "`false` for implicit groups `POS` and `Lieferanten` (name locked).\n`true` for user-created groups. Reorder all groups via\n`PUT /customergroup/order`.\n"
|
||
}
|
||
}
|
||
}
|
||
]
|
||
},
|
||
"Entity": {
|
||
"type": "object",
|
||
"required": [
|
||
"id",
|
||
"customergroupId",
|
||
"firstName",
|
||
"name",
|
||
"number",
|
||
"createdAt",
|
||
"updatedAt"
|
||
],
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"customergroupId": {
|
||
"type": "integer"
|
||
},
|
||
"firstName": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"name": {
|
||
"type": "string"
|
||
},
|
||
"number": {
|
||
"type": "string",
|
||
"nullable": true,
|
||
"description": "Customer number (null for suppliers)"
|
||
},
|
||
"createdAt": {
|
||
"type": "string",
|
||
"format": "date-time"
|
||
},
|
||
"updatedAt": {
|
||
"type": "string",
|
||
"format": "date-time"
|
||
}
|
||
}
|
||
},
|
||
"EntityAddress": {
|
||
"type": "object",
|
||
"required": [
|
||
"id",
|
||
"entityId",
|
||
"addressType"
|
||
],
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"entityId": {
|
||
"type": "integer"
|
||
},
|
||
"firstName": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"name": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"street": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"streetNumber": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"zip": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"city": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"county": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"addressType": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"EntityContact": {
|
||
"type": "object",
|
||
"required": [
|
||
"id",
|
||
"entityId",
|
||
"contactType",
|
||
"contact"
|
||
],
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"entityId": {
|
||
"type": "integer"
|
||
},
|
||
"contactType": {
|
||
"type": "string",
|
||
"description": "e.g. email, phone"
|
||
},
|
||
"contact": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"DeliverySummary": {
|
||
"type": "object",
|
||
"required": [
|
||
"id",
|
||
"number",
|
||
"type",
|
||
"supplierId",
|
||
"date"
|
||
],
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"number": {
|
||
"type": "string",
|
||
"description": "Auto-allocated via nextNumber('delivery')"
|
||
},
|
||
"type": {
|
||
"type": "string",
|
||
"enum": [
|
||
"delivery"
|
||
]
|
||
},
|
||
"supplierId": {
|
||
"type": "integer",
|
||
"nullable": true,
|
||
"description": "manifest.entity_id"
|
||
},
|
||
"externalNumber": {
|
||
"type": "string",
|
||
"nullable": true,
|
||
"description": "User-supplied reference number"
|
||
},
|
||
"date": {
|
||
"type": "string",
|
||
"format": "date-time"
|
||
}
|
||
}
|
||
},
|
||
"DeliveryLine": {
|
||
"type": "object",
|
||
"required": [
|
||
"id",
|
||
"sku",
|
||
"name",
|
||
"pu",
|
||
"amount",
|
||
"net",
|
||
"rebate",
|
||
"tax",
|
||
"comment"
|
||
],
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"sku": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"name": {
|
||
"type": "string",
|
||
"nullable": true,
|
||
"description": "Line item name or custom description"
|
||
},
|
||
"pu": {
|
||
"type": "number",
|
||
"description": "Packaging unit (defaults to 1)"
|
||
},
|
||
"amount": {
|
||
"type": "number"
|
||
},
|
||
"net": {
|
||
"type": "number"
|
||
},
|
||
"rebate": {
|
||
"type": "number"
|
||
},
|
||
"tax": {
|
||
"type": "number",
|
||
"description": "tax.rate (manifestarticle.taxrate)"
|
||
},
|
||
"comment": {
|
||
"type": "string",
|
||
"nullable": true
|
||
}
|
||
}
|
||
},
|
||
"DeliveryLineInput": {
|
||
"type": "object",
|
||
"required": [
|
||
"amount",
|
||
"net",
|
||
"tax"
|
||
],
|
||
"description": "Line item input. Requires `sku` or `name`.",
|
||
"properties": {
|
||
"sku": {
|
||
"type": "string",
|
||
"nullable": true,
|
||
"description": "SKU reference (sku or name is required)"
|
||
},
|
||
"name": {
|
||
"type": "string",
|
||
"nullable": true,
|
||
"description": "Line item name (sku or name is required)"
|
||
},
|
||
"pu": {
|
||
"type": "number",
|
||
"description": "Packaging unit (defaults to 1)"
|
||
},
|
||
"amount": {
|
||
"type": "number"
|
||
},
|
||
"net": {
|
||
"type": "number"
|
||
},
|
||
"tax": {
|
||
"type": "number",
|
||
"description": "Tax rate (resolved via tax.ensure)"
|
||
},
|
||
"rebate": {
|
||
"type": "number",
|
||
"description": "Defaults to 0"
|
||
},
|
||
"comment": {
|
||
"type": "string",
|
||
"nullable": true
|
||
}
|
||
}
|
||
},
|
||
"Delivery": {
|
||
"allOf": [
|
||
{
|
||
"$ref": "#/components/schemas/DeliverySummary"
|
||
},
|
||
{
|
||
"type": "object",
|
||
"required": [
|
||
"lines"
|
||
],
|
||
"properties": {
|
||
"lines": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/DeliveryLine"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
]
|
||
},
|
||
"ManifestLine": {
|
||
"type": "object",
|
||
"required": [
|
||
"id",
|
||
"sku",
|
||
"name",
|
||
"pu",
|
||
"amount",
|
||
"net",
|
||
"rebate",
|
||
"tax",
|
||
"comment"
|
||
],
|
||
"description": "Line shape for quote and order",
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"sku": {
|
||
"type": "string",
|
||
"nullable": true
|
||
},
|
||
"name": {
|
||
"type": "string",
|
||
"nullable": true,
|
||
"description": "Line item name or custom description"
|
||
},
|
||
"pu": {
|
||
"type": "number",
|
||
"description": "Packaging unit (defaults to 1)"
|
||
},
|
||
"amount": {
|
||
"type": "number"
|
||
},
|
||
"net": {
|
||
"type": "number"
|
||
},
|
||
"rebate": {
|
||
"type": "number"
|
||
},
|
||
"tax": {
|
||
"type": "number",
|
||
"description": "tax.rate (manifestarticle.taxrate)"
|
||
},
|
||
"comment": {
|
||
"type": "string",
|
||
"nullable": true
|
||
}
|
||
}
|
||
},
|
||
"ManifestLineInput": {
|
||
"type": "object",
|
||
"required": [
|
||
"amount",
|
||
"net",
|
||
"tax"
|
||
],
|
||
"description": "Line item input. Requires `sku` or `name`.",
|
||
"properties": {
|
||
"sku": {
|
||
"type": "string",
|
||
"nullable": true,
|
||
"description": "SKU reference (sku or name is required)"
|
||
},
|
||
"name": {
|
||
"type": "string",
|
||
"nullable": true,
|
||
"description": "Line item name (sku or name is required)"
|
||
},
|
||
"pu": {
|
||
"type": "number",
|
||
"description": "Packaging unit (defaults to 1)"
|
||
},
|
||
"amount": {
|
||
"type": "number"
|
||
},
|
||
"net": {
|
||
"type": "number"
|
||
},
|
||
"tax": {
|
||
"type": "number",
|
||
"description": "Tax rate (resolved via tax.ensure)"
|
||
},
|
||
"rebate": {
|
||
"type": "number",
|
||
"description": "Defaults to 0"
|
||
},
|
||
"comment": {
|
||
"type": "string",
|
||
"nullable": true
|
||
}
|
||
}
|
||
},
|
||
"QuoteSummary": {
|
||
"type": "object",
|
||
"required": [
|
||
"id",
|
||
"number",
|
||
"type",
|
||
"entityId",
|
||
"date"
|
||
],
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"number": {
|
||
"type": "string"
|
||
},
|
||
"type": {
|
||
"type": "string",
|
||
"enum": [
|
||
"quote"
|
||
]
|
||
},
|
||
"entityId": {
|
||
"type": "integer",
|
||
"nullable": true,
|
||
"description": "Customer (manifest.entity_id)"
|
||
},
|
||
"date": {
|
||
"type": "string",
|
||
"format": "date-time",
|
||
"description": "Document date / valid-until"
|
||
}
|
||
}
|
||
},
|
||
"Quote": {
|
||
"allOf": [
|
||
{
|
||
"$ref": "#/components/schemas/QuoteSummary"
|
||
},
|
||
{
|
||
"type": "object",
|
||
"required": [
|
||
"lines"
|
||
],
|
||
"properties": {
|
||
"lines": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/ManifestLine"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
]
|
||
},
|
||
"OrderSummary": {
|
||
"type": "object",
|
||
"required": [
|
||
"id",
|
||
"number",
|
||
"type",
|
||
"entityId",
|
||
"date"
|
||
],
|
||
"properties": {
|
||
"id": {
|
||
"type": "integer"
|
||
},
|
||
"number": {
|
||
"type": "string"
|
||
},
|
||
"type": {
|
||
"type": "string",
|
||
"enum": [
|
||
"order"
|
||
]
|
||
},
|
||
"entityId": {
|
||
"type": "integer",
|
||
"nullable": true,
|
||
"description": "Customer (manifest.entity_id)"
|
||
},
|
||
"date": {
|
||
"type": "string",
|
||
"format": "date-time",
|
||
"description": "Document date / expected delivery"
|
||
}
|
||
}
|
||
},
|
||
"Order": {
|
||
"allOf": [
|
||
{
|
||
"$ref": "#/components/schemas/OrderSummary"
|
||
},
|
||
{
|
||
"type": "object",
|
||
"required": [
|
||
"lines"
|
||
],
|
||
"properties": {
|
||
"lines": {
|
||
"type": "array",
|
||
"items": {
|
||
"$ref": "#/components/schemas/ManifestLine"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
]
|
||
},
|
||
"ArticleImage": {
|
||
"type": "object",
|
||
"required": [
|
||
"picId",
|
||
"sort",
|
||
"full",
|
||
"thumb",
|
||
"hash",
|
||
"thumbHash"
|
||
],
|
||
"properties": {
|
||
"picId": {
|
||
"type": "integer"
|
||
},
|
||
"sort": {
|
||
"type": "integer"
|
||
},
|
||
"full": {
|
||
"type": "object",
|
||
"required": [
|
||
"w",
|
||
"h",
|
||
"size"
|
||
],
|
||
"properties": {
|
||
"w": {
|
||
"type": "integer"
|
||
},
|
||
"h": {
|
||
"type": "integer"
|
||
},
|
||
"size": {
|
||
"type": "integer",
|
||
"description": "Byte length of full AVIF (as-is when already AVIF ≤1000×1000)"
|
||
}
|
||
}
|
||
},
|
||
"thumb": {
|
||
"type": "object",
|
||
"required": [
|
||
"w",
|
||
"h"
|
||
],
|
||
"properties": {
|
||
"w": {
|
||
"type": "integer"
|
||
},
|
||
"h": {
|
||
"type": "integer"
|
||
}
|
||
}
|
||
},
|
||
"hash": {
|
||
"type": "string",
|
||
"description": "SHA-256 hex of full image bytes"
|
||
},
|
||
"thumbHash": {
|
||
"type": "string",
|
||
"description": "Base64 thumbhash of thumb preview"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|