Add company settings and number sequence API routes

- Add CompanySetting routes (list, get, set) backed by company-scoped store
- Add Number routes (list, update) for managing number sequences
- Register new store namespaces for numbers and companysetting
- Add CompanySetting and NumberSequence schemas to OpenAPI
- Regenerate API docs and index for the new routes
This commit is contained in:
seb
2026-08-08 09:01:28 +02:00
parent e6c24654dd
commit 57420ac0db
14 changed files with 1279 additions and 0 deletions

View File

@@ -36,6 +36,10 @@
"name": "Company",
"description": "Company CRUD"
},
{
"name": "CompanySetting",
"description": "Company-scoped settings"
},
{
"name": "Unit",
"description": "Company-scoped units of measure"
@@ -2387,6 +2391,191 @@
}
}
},
"/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": [
@@ -4757,6 +4946,268 @@
}
}
},
"/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": [
@@ -8013,6 +8464,22 @@
}
}
},
"CompanySetting": {
"type": "object",
"required": [
"key",
"value"
],
"properties": {
"key": {
"type": "string"
},
"value": {
"type": "string",
"nullable": true
}
}
},
"Category": {
"type": "object",
"properties": {
@@ -8119,6 +8586,36 @@
}
}
},
"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": [