Compare commits
241 Commits
c2d3cdb088
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a45b43f50 | ||
|
|
9f61c7dee2 | ||
|
|
980578e1e4 | ||
|
|
ce4dc15aac | ||
|
|
da785da2dd | ||
|
|
24563964a6 | ||
|
|
44e91a6973 | ||
|
|
de2957acf1 | ||
|
|
17df88ced5 | ||
|
|
d3209d73c1 | ||
|
|
518106ede2 | ||
|
|
833cee5040 | ||
|
|
aeb79c1acb | ||
|
|
ff185a59dd | ||
|
|
689f12ba69 | ||
|
|
bf081f875d | ||
|
|
8f9c941699 | ||
|
|
e6c15dd053 | ||
|
|
8a5a307c50 | ||
|
|
43d7049a7b | ||
|
|
c60b899c4d | ||
|
|
e8a5322775 | ||
|
|
cb30352b57 | ||
|
|
5d6b31de15 | ||
|
|
8827d6fc14 | ||
|
|
16f70603bc | ||
|
|
ce7f5aad2a | ||
|
|
26adfc5aa9 | ||
|
|
0480a5dcbe | ||
|
|
dd5c9646b3 | ||
|
|
5a0b603306 | ||
|
|
72d05a34d0 | ||
|
|
8fece9d916 | ||
|
|
5fac537268 | ||
|
|
61529023b0 | ||
|
|
2df7c59feb | ||
|
|
02301ff172 | ||
|
|
ab6fcc107f | ||
|
|
bc1340419c | ||
|
|
fbe3105dc9 | ||
|
|
e82ea15d02 | ||
|
|
527e55fec2 | ||
|
|
e1d27b44a7 | ||
|
|
a7258abf35 | ||
|
|
1b432cb6c6 | ||
|
|
0303f8121c | ||
|
|
d151bb5fe3 | ||
|
|
676a40d4fe | ||
|
|
005b48ff66 | ||
|
|
72e6e79d20 | ||
|
|
6a80473594 | ||
|
|
bcdfba1742 | ||
|
|
907ee998c0 | ||
|
|
158748c5e6 | ||
|
|
dc9fcdad96 | ||
|
|
116b51f0a9 | ||
|
|
c937bad151 | ||
|
|
1881830b0a | ||
|
|
01d6aa0e3f | ||
|
|
ff73750092 | ||
|
|
e7884c03ef | ||
|
|
1c5385061c | ||
|
|
1ac6435c3f | ||
|
|
80ecad6ddd | ||
|
|
97cae698db | ||
|
|
88ed2be452 | ||
|
|
b3368a53f8 | ||
|
|
e490ad78b5 | ||
|
|
41c532e2ca | ||
|
|
b6f68f04a4 | ||
|
|
2cc166bbec | ||
|
|
2dea4e86f1 | ||
|
|
74d3ba5aeb | ||
|
|
69388c789f | ||
|
|
a5a9e51898 | ||
|
|
5d6d07e8bf | ||
|
|
fb2dafd920 | ||
|
|
c07e4dc083 | ||
|
|
369fbb2a70 | ||
|
|
464021318a | ||
|
|
9929e322a3 | ||
|
|
ebbeb6437a | ||
|
|
3f2bdce5ad | ||
|
|
1a5e3f23e2 | ||
|
|
e83116c83d | ||
|
|
7fcaca318a | ||
|
|
76235b0e1f | ||
|
|
a6cb52e415 | ||
|
|
36a135011f | ||
|
|
a27f507138 | ||
|
|
e83be2ca21 | ||
|
|
1a0023b775 | ||
|
|
1aa1582682 | ||
|
|
be834412e7 | ||
|
|
5be5644329 | ||
|
|
ada4d93f65 | ||
|
|
ee0eb2820e | ||
|
|
e4e4d2dcf0 | ||
|
|
63f4c3a0e4 | ||
|
|
b7058df5d2 | ||
|
|
5a5981dc07 | ||
|
|
fde381937c | ||
|
|
de3e74c928 | ||
|
|
a04094a7be | ||
|
|
3f540de768 | ||
|
|
3450552f7c | ||
|
|
6fbdeaa070 | ||
|
|
1bf99deeb9 | ||
|
|
e14a8ee8f0 | ||
|
|
c958ecf604 | ||
|
|
f571328e0c | ||
|
|
a717a05e8d | ||
|
|
c06a5e3413 | ||
|
|
5e4cb5a3f8 | ||
|
|
0ec993a0b6 | ||
|
|
69a9781cb6 | ||
|
|
7445a573ab | ||
|
|
9c50d9efc3 | ||
|
|
36d62138d4 | ||
|
|
0463003681 | ||
|
|
d0a2ede749 | ||
|
|
badce6819c | ||
|
|
832cfaca41 | ||
|
|
a01b42b24b | ||
|
|
04943fb7a6 | ||
|
|
e085e064ee | ||
|
|
df846f4f06 | ||
|
|
4bd977ed12 | ||
|
|
1778815a1e | ||
|
|
856692b66c | ||
|
|
5e9b168e0d | ||
|
|
bee21d2c20 | ||
|
|
7197a6ddb7 | ||
|
|
988b4848cf | ||
|
|
c61df92d63 | ||
|
|
fd21cb7319 | ||
|
|
da1254b0b1 | ||
|
|
8e841d2c23 | ||
|
|
0fe672a054 | ||
|
|
d7f7d669f9 | ||
|
|
188cd71f63 | ||
|
|
de6c4987b9 | ||
|
|
d05ba9276f | ||
|
|
75925ab35c | ||
|
|
52f49fb651 | ||
|
|
2f0c4aced1 | ||
|
|
99ac40e6fa | ||
|
|
7f28a05ed3 | ||
|
|
3b875c9f9a | ||
|
|
f506dd35be | ||
|
|
5ed634990e | ||
|
|
bed19f8b10 | ||
|
|
e083d66a75 | ||
|
|
a560a78f4d | ||
|
|
144bf0ca43 | ||
|
|
50f41bca8c | ||
|
|
2165afcc02 | ||
|
|
084b1b285e | ||
|
|
9a4e2e30df | ||
|
|
0039b80d31 | ||
|
|
78ad8f12e4 | ||
|
|
29a07d132c | ||
|
|
8af63d6bf8 | ||
|
|
508e1b6d7a | ||
|
|
e462a3d250 | ||
|
|
529b142341 | ||
|
|
f331625d63 | ||
|
|
8c55f899b5 | ||
|
|
bdd7ebf5b8 | ||
|
|
507dffa567 | ||
|
|
0ea7da4f3a | ||
|
|
d880273629 | ||
|
|
9c54a8a1d3 | ||
|
|
04e10cffdb | ||
|
|
90b782e500 | ||
|
|
37c58648db | ||
|
|
5fb8df6f6a | ||
|
|
80450e8748 | ||
|
|
e230c4cf12 | ||
|
|
6c821d381e | ||
|
|
1991c0b529 | ||
|
|
8ccdad18f4 | ||
|
|
f9ed4fe75d | ||
|
|
74b24686bc | ||
|
|
11f6a8fc0f | ||
|
|
b7b5d554d8 | ||
|
|
c3d4fa5cd9 | ||
|
|
2de2bc0d90 | ||
|
|
8acca998bc | ||
|
|
97540fb745 | ||
|
|
2f92199d7f | ||
|
|
3d009ba216 | ||
|
|
f743ace6af | ||
|
|
cc77bf4377 | ||
|
|
77367b7716 | ||
|
|
3748020b03 | ||
|
|
c35126bb11 | ||
|
|
713c31a21e | ||
|
|
358a023b10 | ||
|
|
06dc4629da | ||
|
|
88ecf5bf5c | ||
|
|
280ed1b2b8 | ||
|
|
1f09e620e3 | ||
|
|
4429294d8b | ||
|
|
a2e446af47 | ||
|
|
15a17574c3 | ||
|
|
7fd8d5d704 | ||
|
|
fe7be79b90 | ||
|
|
1eb5cb4e59 | ||
|
|
9443f20820 | ||
|
|
e72467da50 | ||
|
|
1a51944e59 | ||
|
|
663589a450 | ||
|
|
0621416aa8 | ||
|
|
1149ffddcb | ||
|
|
8262e35e53 | ||
|
|
b5e7643e12 | ||
|
|
47c61c9f2a | ||
|
|
29057e8d7d | ||
|
|
6a8716a6ef | ||
|
|
f110173e57 | ||
|
|
188b8efe09 | ||
|
|
7fb7435779 | ||
|
|
67678038c0 | ||
|
|
eed10ea38d | ||
|
|
c012b82af3 | ||
|
|
7f8661be3e | ||
|
|
d4e2b05cb0 | ||
|
|
ac10840fb6 | ||
|
|
cd6879f3b7 | ||
|
|
daf2d0525f | ||
|
|
848ca0aff5 | ||
|
|
de7afbcbe9 | ||
|
|
728b3ec172 | ||
|
|
011d84c3c1 | ||
|
|
969c064f3a | ||
|
|
0493b218d7 | ||
|
|
c0a743654f | ||
|
|
f4ae04af15 | ||
|
|
bc323f2891 | ||
|
|
8111c3f377 |
42
.agents/skills/database-schema-updates/SKILL.md
Normal file
42
.agents/skills/database-schema-updates/SKILL.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
name: database-schema-updates
|
||||||
|
description: "Use when changing or diagnosing the Dash database schema, applying a local schema patch, or rerunning the off-repository development migration script."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Database Schema Updates
|
||||||
|
|
||||||
|
Use this workflow for schema changes and local database updates in the Dashback + `dashdbschema` setup.
|
||||||
|
|
||||||
|
## Project map
|
||||||
|
|
||||||
|
- The schema/store repository is the sibling [`dashdbschema`](../../../../dashdbschema) (`libdashstore`).
|
||||||
|
- MSSQL schema DDL: [`schema.js`](../../../../dashdbschema/src/providers/mssql/schema.js).
|
||||||
|
- SQLite schema DDL: [`schema.js`](../../../../dashdbschema/src/providers/sqlite3/schema.js).
|
||||||
|
- Provider behavior: `../../../../dashdbschema/src/providers/{mssql,sqlite3}/`.
|
||||||
|
- Store implementation: `../../../../dashdbschema/src/store/`; SQL queries: `../../../../dashdbschema/src/providers/mssql/queries/`.
|
||||||
|
- Schema/store overview and conventions: [`README.md`](../../../../dashdbschema/README.md) and [`AGENTS.md`](../../../../dashdbschema/AGENTS.md).
|
||||||
|
- Store API documentation index: [`API.md`](../../../../dashdbschema/API.md), with per-domain details in `../../../../dashdbschema/API.<module>.md`.
|
||||||
|
- Dashback database configuration: `src/config/env.js` and the local, untracked `.env`. SQLite defaults to `data/dash.db` unless `SQLITE_PATH` or `SQLITE_FILENAME` overrides it; `DB_PROVIDER` determines the active provider.
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- Do not add migration/upgrade code, migration scripts, or migration-only DDL to a Git repository. The checked-in schema must describe the desired state for a fresh database.
|
||||||
|
- Existing local-development databases may need separate schema-patch scripts maintained in the Git-ignored `data/` folder. Name them `NNN-description.mjs`, using a three-digit sequence that increments from the highest existing number (for example, `001-schema-patch.mjs`, then `002-add-index.mjs`). Never reuse a number or renumber existing scripts; retain older scripts so patches can be tracked and rerun in order. Do not put these scripts in Git-tracked folders.
|
||||||
|
- In this dashback workspace, sibling repositories are read-only. Read their schema and conventions, but do not edit, install dependencies, or run their scripts unless the user explicitly authorizes work in that sibling repository.
|
||||||
|
- Never use `purge-db` or `reset-db` on a database that contains data: those commands purge tables. Do not assume that `initialize()` upgrades existing tables.
|
||||||
|
- Do not print credentials or expose `.env` values in tool output.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. Confirm which schema/provider and environment the change concerns. Read the applicable schema, provider, and domain query/store code, plus the relevant repository instructions. For SQL changes, check both MSSQL and SQLite behavior where applicable.
|
||||||
|
2. Make the checked-in schema represent the desired fresh-database shape. Respect the sibling-repository read-only boundary; ask the user to coordinate or authorize a `dashdbschema` change rather than editing across that boundary.
|
||||||
|
3. For an existing local dev database, identify the numbered patch script in `data/` and verify its exact path and contents before running it. Create new local-only patch scripts with the next consecutive three-digit number; never overwrite or renumber existing patches. Confirm the target is the local development database by checking the active provider and configured database path/name without displaying secrets. If the script, target, or safety of rerunning it is unclear, ask the user rather than guessing.
|
||||||
|
4. Preserve local data. For a file-backed SQLite database, make a backup before applying a script that changes it. Do not run against production, a shared database, or an unconfirmed target. Do not stop or start the API as part of this workflow.
|
||||||
|
5. Run or rerun the requested numbered script against the confirmed local dev database from its established location and documented invocation; do not embed credentials in command arguments or logs. Report the result and any errors explicitly.
|
||||||
|
6. Verify the intended schema change against the local dev database using a targeted, read-only schema check. Do not claim success if the script failed or verification was not possible.
|
||||||
|
|
||||||
|
## Important notes
|
||||||
|
|
||||||
|
- The schema source is DDL for new databases, not an upgrade mechanism. Keep fresh database initialization aligned with any manual/local schema changes.
|
||||||
|
- [`no-schema-migrations.mdc`](../../../../dashdbschema/.cursor/rules/no-schema-migrations.mdc) documents the no-migration-code convention. Its reset-db workflow is destructive; it is not a substitute for the separate local dev migration script.
|
||||||
|
- Local patches belong in the Git-ignored `data/` directory and use continuous three-digit numbering. If the correct database target or script operation cannot be verified, ask the user rather than guessing.
|
||||||
89
.agents/skills/naming-conventions-and-style/SKILL.md
Normal file
89
.agents/skills/naming-conventions-and-style/SKILL.md
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
---
|
||||||
|
name: naming-conventions-and-style
|
||||||
|
description: "Use when: improving variable, function, module, or API names; refactoring code for clarity; choosing consistent naming in the dashback backend."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Style
|
||||||
|
|
||||||
|
use vs code region markers in code files
|
||||||
|
use:
|
||||||
|
// MARK: calls startApi from ./api/index.js
|
||||||
|
atop important functions
|
||||||
|
|
||||||
|
# Naming Conventions — dashback
|
||||||
|
|
||||||
|
Apply these conventions when writing or refactoring code in this project.
|
||||||
|
|
||||||
|
## Core rules
|
||||||
|
|
||||||
|
- Prefer clear domain names over vague generic names.
|
||||||
|
- Name functions as actions, not nouns.
|
||||||
|
- Name variables and parameters by what they represent, not by their implementation detail.
|
||||||
|
- Prefer explicit names over abbreviations unless the abbreviation is standard in the project.
|
||||||
|
- Keep names consistent across the same subsystem.
|
||||||
|
|
||||||
|
## Function naming
|
||||||
|
|
||||||
|
- Use verb-based names for routines that do work: `registerRoutes`, `buildApiApp`, `listenOnPort`, `resolveUser`, `validateInput`.
|
||||||
|
- Use `register`, `build`, `create`, `resolve`, `configure`, `respond`, `validate`, and `render` when the function performs an action.
|
||||||
|
- Avoid vague names like `load`, `walk`, `handle`, `do`, `process`, `thing`, and `data` when a more specific name is available.
|
||||||
|
- When a function builds a response object, prefer names like `respondWith...`, `build...Response`, or `create...Payload`.
|
||||||
|
|
||||||
|
## Variable naming
|
||||||
|
|
||||||
|
- Prefer descriptive nouns or noun phrases: `serverStartedAt`, `startupPromise`, `localPrivateHostPattern`.
|
||||||
|
- Use boolean names that read like a question when possible: `isAllowedCorsOrigin`, `hasSelectedCompany`, `isDocsRequest`.
|
||||||
|
- Use `statusCode` instead of `status` when a numeric HTTP status is involved.
|
||||||
|
- Use `origin` and `request`/`req` precisely; avoid ambiguous names like `body` when `payload` or `userData` is clearer.
|
||||||
|
|
||||||
|
## Module naming
|
||||||
|
|
||||||
|
- Prefer names that describe the module’s responsibility, not incidental implementation details.
|
||||||
|
- For API helper modules, names should communicate the response shape or purpose: `meJson` → `respondWithAuthenticatedUser` or `buildCurrentUserResponse`.
|
||||||
|
- Use consistent file-level naming patterns across the same feature area.
|
||||||
|
|
||||||
|
## File naming
|
||||||
|
|
||||||
|
- Keep each route responsibility in its own file under `src/api/routes`.
|
||||||
|
- Use one route module per resource or workflow, not multiple unrelated endpoints in a single file.
|
||||||
|
- Prefer a single clear file name per route group, for example `auth/me.js`, `company/list.js`, `article/create.js`, or `journal/entries.js`.
|
||||||
|
- Match the file name to the route’s primary resource or action so the file’s purpose is obvious from the directory tree.
|
||||||
|
- Avoid generic names like `index.js`, `utils.js`, or `helpers.js` for route modules unless the file is truly the entry point for a module group.
|
||||||
|
- Keep nested route directories aligned with the URL hierarchy when it improves clarity, e.g. `auth/me.js` for `/auth/me`.
|
||||||
|
|
||||||
|
## API and HTTP naming
|
||||||
|
|
||||||
|
- Prefer `registerRoutes` over `loadRoutes` when the function actively registers route modules on the app.
|
||||||
|
- Prefer `buildApiApp` over `createApi` when the function constructs the Express app object.
|
||||||
|
- Prefer `isAllowedCorsOrigin` over `isAllowedOrigin` for clarity around the specific policy being enforced.
|
||||||
|
- When a helper adds Google client info to a response, include the domain concept in the name, e.g. `respondWithAuthenticatedUser`.
|
||||||
|
|
||||||
|
## Examples from this codebase
|
||||||
|
|
||||||
|
Good:
|
||||||
|
- `registerRoutes`
|
||||||
|
- `buildApiApp`
|
||||||
|
- `listenOnPort`
|
||||||
|
- `startupPromise`
|
||||||
|
- `serverStartedAt`
|
||||||
|
- `respondWithAuthenticatedUser`
|
||||||
|
- `isAllowedCorsOrigin`
|
||||||
|
- `LOCAL_PRIVATE_HOST_PATTERN`
|
||||||
|
|
||||||
|
Avoid:
|
||||||
|
- `loadRoutes` when the function actually registers route modules
|
||||||
|
- `walk` in a route auto-loader unless it is explicitly about recursive traversal and the context is clear
|
||||||
|
- `starting` and `startedAt` when `startupPromise` and `serverStartedAt` are more specific
|
||||||
|
- `meJson` when the function is really a response builder for authenticated-user payloads
|
||||||
|
- generic names like `data`, `thing`, `value`, `handle`, `doWork`
|
||||||
|
|
||||||
|
## Review checklist
|
||||||
|
|
||||||
|
Before finalizing a rename, ask:
|
||||||
|
- Does the name describe the actual responsibility?
|
||||||
|
- Does it read clearly in the local code context?
|
||||||
|
- Is it consistent with neighboring names?
|
||||||
|
- Is there any ambiguity about the action, domain, or response shape?
|
||||||
|
- Is it specific enough that a teammate can infer the purpose without reading the implementation?
|
||||||
|
|
||||||
|
Use these rules as the default naming standard for backend refactors and new code in the project.
|
||||||
17
.dockerignore
Normal file
17
.dockerignore
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
node_modules/*
|
||||||
|
!node_modules/better-sqlite3
|
||||||
|
!node_modules/canvas
|
||||||
|
!node_modules/@img
|
||||||
|
!node_modules/sharp
|
||||||
|
dist/*
|
||||||
|
!dist/dashback
|
||||||
|
data
|
||||||
|
certs
|
||||||
|
.git
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
*.log
|
||||||
|
docs/api
|
||||||
|
scratch
|
||||||
|
|
||||||
25
.env.example
25
.env.example
@@ -1,7 +1,11 @@
|
|||||||
# Server
|
# Server
|
||||||
|
# Host interface to listen on (defaults to 127.0.0.1 for local only, use 0.0.0.0 for all network interfaces)
|
||||||
|
HOST=127.0.0.1
|
||||||
PORT=4208
|
PORT=4208
|
||||||
# HTTPS server port (defaults to 8443, set to 0 or false to disable)
|
# HTTPS server port (defaults to 8443, set to 0 or false to disable)
|
||||||
HTTPS_PORT=8443
|
HTTPS_PORT=8443
|
||||||
|
# Backend Monitor Interface port (served when backend is started with npm run dev)
|
||||||
|
MONITOR_PORT=4210
|
||||||
# Optional custom TLS cert/key paths (defaults to auto-generated certs/key.pem and certs/cert.pem)
|
# Optional custom TLS cert/key paths (defaults to auto-generated certs/key.pem and certs/cert.pem)
|
||||||
# HTTPS_KEY_PATH=./certs/key.pem
|
# HTTPS_KEY_PATH=./certs/key.pem
|
||||||
# HTTPS_CERT_PATH=./certs/cert.pem
|
# HTTPS_CERT_PATH=./certs/cert.pem
|
||||||
@@ -13,6 +17,7 @@ CORS_ORIGINS=http://localhost:5173
|
|||||||
# Auth — Google OAuth client ID and JWT signing secret
|
# Auth — Google OAuth client ID and JWT signing secret
|
||||||
GOOGLE_CLIENT_ID=
|
GOOGLE_CLIENT_ID=
|
||||||
JWT_SECRET=fallback_secret_please_change_in_production
|
JWT_SECRET=fallback_secret_please_change_in_production
|
||||||
|
# Auto-create user on first login (when false, only allowlisted or invited emails can sign in / create an account)
|
||||||
IMPLICIT_CREATE=false
|
IMPLICIT_CREATE=false
|
||||||
|
|
||||||
# Database Provider — 'sqlite' or 'mssql' (defaults to 'mssql', or 'sqlite' if SQLITE_PATH is set)
|
# Database Provider — 'sqlite' or 'mssql' (defaults to 'mssql', or 'sqlite' if SQLITE_PATH is set)
|
||||||
@@ -42,4 +47,24 @@ OPENROUTER_FREE_KEY=sk-or-v1-3d3c4e
|
|||||||
OPENROUTER_FREE_MODEL=openrouter/free
|
OPENROUTER_FREE_MODEL=openrouter/free
|
||||||
OPENROUTER_PAID_MODEL=openai/gpt-6-luna:floor
|
OPENROUTER_PAID_MODEL=openai/gpt-6-luna:floor
|
||||||
|
|
||||||
|
# Backup Service
|
||||||
|
# BACKUP_ENABLED=true
|
||||||
|
# BACKUP_DIR=./data/backups
|
||||||
|
# BACKUP_INTERVAL_MS=3600000
|
||||||
|
# BACKUP_HOURLY_RETENTION=50
|
||||||
|
# BACKUP_DAILY_RETENTION=50
|
||||||
|
|
||||||
|
# Redis (Healthcheck Latency Metric)
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PORT=6379
|
||||||
|
REDIS_PASSWORD=
|
||||||
|
REDIS_DB=0
|
||||||
|
REDIS_KEY=healthcheck:latency_ms
|
||||||
|
# REDIS_URL=redis://127.0.0.1:6379
|
||||||
|
|
||||||
|
# Health Monitor Service
|
||||||
|
# HEALTH_MONITOR_ENABLED=true
|
||||||
|
# HEALTH_MONITOR_INTERVAL_MS=60000
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,3 +4,5 @@ data/
|
|||||||
logs/
|
logs/
|
||||||
print_spool/
|
print_spool/
|
||||||
certs/
|
certs/
|
||||||
|
output/
|
||||||
|
dist/
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ ESM (`type: module`), Express 5, Node. Depends on `libdashstore` via `file:../da
|
|||||||
cp .env.example .env && npm install
|
cp .env.example .env && npm install
|
||||||
npm start # PORT 4208 (HTTPS 8443)
|
npm start # PORT 4208 (HTTPS 8443)
|
||||||
npm run docs:api # regenerates docs/api/routes.md + docs/api/routes-all.md + docs/api/routes/ (no openapi.json on disk — /docs/ builds it live)
|
npm run docs:api # regenerates docs/api/routes.md + docs/api/routes-all.md + docs/api/routes/ (no openapi.json on disk — /docs/ builds it live)
|
||||||
|
npm run api-graph # regenerates api-graph.svg + api-graph.dot
|
||||||
npm run manage-users # email allowlist
|
npm run manage-users # email allowlist
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
25
Dockerfile
Normal file
25
Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM debian:12-slim AS syslibs
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
libatomic1 libuuid1 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
FROM gcr.io/distroless/cc-debian12
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Shared dynamic libraries needed by Node.js SEA and native addons (better-sqlite3)
|
||||||
|
COPY --from=syslibs /usr/lib/x86_64-linux-gnu/libatomic.so* /usr/lib/x86_64-linux-gnu/
|
||||||
|
COPY --from=syslibs /usr/lib/x86_64-linux-gnu/libuuid.so* /usr/lib/x86_64-linux-gnu/
|
||||||
|
|
||||||
|
# Set production defaults: port 4208
|
||||||
|
ENV RUNNING_IN_DOCKER=true \
|
||||||
|
NODE_ENV=production \
|
||||||
|
HOST=0.0.0.0 \
|
||||||
|
PORT=4208 \
|
||||||
|
HTTPS_PORT=0 \
|
||||||
|
SQLITE_PATH=/app/data/dash.db \
|
||||||
|
STORAGE_DIR=/app/data/storage \
|
||||||
|
BACKUP_DIR=/app/data/backups
|
||||||
|
|
||||||
|
CMD ["./dist/dashback"]
|
||||||
13
README.md
13
README.md
@@ -27,9 +27,20 @@ Docs are generated from `@openapi` JSDoc on route handlers:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run docs:api
|
npm run docs:api
|
||||||
|
npm run api-graph # regenerate api-graph.svg + api-graph.dot
|
||||||
```
|
```
|
||||||
|
|
||||||
That writes `docs/api/routes.md`, `docs/api/routes-all.md`, and per-route Markdown under `docs/api/routes/`. There is no generated `openapi.json` on disk — the interactive Swagger UI at `/docs/` builds the spec from the same `@openapi` JSDoc at runtime (`src/api/openapiSpec.js`).
|
That writes `docs/api/routes.md`, `docs/api/routes-all.md`, and per-route Markdown under `docs/api/routes/`. `npm run api-graph` generates `api-graph.svg` and `api-graph.dot` visualizing the API resources, endpoints, and dataflow relationships. There is no generated `openapi.json` on disk — the interactive Swagger UI at `/docs/` builds the spec from the same `@openapi` JSDoc at runtime (`src/api/openapiSpec.js`).
|
||||||
|
|
||||||
|
### Function review files
|
||||||
|
|
||||||
|
Create one JavaScript file per function in the project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run review:functions
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated files are written to `output/` and include each function's source filename and line numbers. The same directory also contains `functions.txt`, an index of source filenames, line numbers, and function signatures. Existing generated function files and the index are replaced each time the command runs. Dependencies, runtime data, and the output directory itself are excluded from the scan.
|
||||||
|
|
||||||
### Brands (company-scoped, `selectedCompanyId` aus der Session)
|
### Brands (company-scoped, `selectedCompanyId` aus der Session)
|
||||||
|
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ export function createBashTool(googleAuthId) {
|
|||||||
|
|
||||||
| Consideration | Ephemeral (`docker run --rm`) | Persistent Container (`docker exec`) |
|
| Consideration | Ephemeral (`docker run --rm`) | Persistent Container (`docker exec`) |
|
||||||
| :--- | :--- | :--- |
|
| :--- | :--- | :--- |
|
||||||
| **Startup Overhead** | ~200–400ms per command | ~20–50ms per command |
|
| **Startup Overhead** | ~200-400ms per command | ~20-50ms per command |
|
||||||
| **State Cleanliness** | Completely fresh OS environment every call | Processes/background jobs remain running |
|
| **State Cleanliness** | Completely fresh OS environment every call | Processes/background jobs remain running |
|
||||||
| **Lifecycle Complexity** | None. Docker handles lifecycle and cleanup | Requires container pool management and reaper |
|
| **Lifecycle Complexity** | None. Docker handles lifecycle and cleanup | Requires container pool management and reaper |
|
||||||
| **Failure Recovery** | Automatic | If container crashes, must be recreated |
|
| **Failure Recovery** | Automatic | If container crashes, must be recreated |
|
||||||
|
|||||||
176
api-graph.dot
Normal file
176
api-graph.dot
Normal file
File diff suppressed because one or more lines are too long
2727
api-graph.svg
Normal file
2727
api-graph.svg
Normal file
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 330 KiB |
1066
artikeltabelle.csv
1066
artikeltabelle.csv
File diff suppressed because it is too large
Load Diff
@@ -32,8 +32,23 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
|
|||||||
|
|
||||||
| File | Endpoint |
|
| File | Endpoint |
|
||||||
|------|----------|
|
|------|----------|
|
||||||
|
| [`routes/.well-known/oauth-authorization-server.md`](routes/.well-known/oauth-authorization-server.md) | `GET /.well-known/oauth-authorization-server` |
|
||||||
|
| [`routes/.well-known/oauth-protected-resource.md`](routes/.well-known/oauth-protected-resource.md) | `GET /.well-known/oauth-protected-resource` |
|
||||||
|
| [`routes/account.get.md`](routes/account.get.md) | `GET /account` |
|
||||||
|
| [`routes/account.post.md`](routes/account.post.md) | `POST /account` |
|
||||||
|
| [`routes/account/_id.md`](routes/account/_id.md) | `DELETE /account/{id}` |
|
||||||
|
| [`routes/agent/events.md`](routes/agent/events.md) | `GET /agent/events` |
|
||||||
|
| [`routes/agent/pair.get.md`](routes/agent/pair.get.md) | `GET /agent/pair` |
|
||||||
|
| [`routes/agent/pair.post.md`](routes/agent/pair.post.md) | `POST /agent/pair` |
|
||||||
|
| [`routes/agent/state.md`](routes/agent/state.md) | `POST /agent/state` |
|
||||||
|
| [`routes/agentprinter.md`](routes/agentprinter.md) | `GET /agentprinter` |
|
||||||
|
| [`routes/agentprinter/order.md`](routes/agentprinter/order.md) | `PUT /agentprinter/order` |
|
||||||
|
| [`routes/agentprinter/_id.delete.md`](routes/agentprinter/_id.delete.md) | `DELETE /agentprinter/{id}` |
|
||||||
|
| [`routes/agentprinter/_id.get.md`](routes/agentprinter/_id.get.md) | `GET /agentprinter/{id}` |
|
||||||
|
| [`routes/agentprinter/_id.patch.md`](routes/agentprinter/_id.patch.md) | `PATCH /agentprinter/{id}` |
|
||||||
| [`routes/article/create.md`](routes/article/create.md) | `POST /article` |
|
| [`routes/article/create.md`](routes/article/create.md) | `POST /article` |
|
||||||
| [`routes/article/export.zip.md`](routes/article/export.zip.md) | `GET /article/export.zip` |
|
| [`routes/article/export.zip.md`](routes/article/export.zip.md) | `GET /article/export.zip` |
|
||||||
|
| [`routes/article/history-list.md`](routes/article/history-list.md) | `GET /article/history` |
|
||||||
| [`routes/article/delete.md`](routes/article/delete.md) | `DELETE /article/{sku}` |
|
| [`routes/article/delete.md`](routes/article/delete.md) | `DELETE /article/{sku}` |
|
||||||
| [`routes/article/get.md`](routes/article/get.md) | `GET /article/{sku}` |
|
| [`routes/article/get.md`](routes/article/get.md) | `GET /article/{sku}` |
|
||||||
| [`routes/article/update.md`](routes/article/update.md) | `PATCH /article/{sku}` |
|
| [`routes/article/update.md`](routes/article/update.md) | `PATCH /article/{sku}` |
|
||||||
@@ -77,15 +92,30 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
|
|||||||
| [`routes/attributename/_id.patch.md`](routes/attributename/_id.patch.md) | `PATCH /attributename/{id}` |
|
| [`routes/attributename/_id.patch.md`](routes/attributename/_id.patch.md) | `PATCH /attributename/{id}` |
|
||||||
| [`routes/attributevalue/_id.delete.md`](routes/attributevalue/_id.delete.md) | `DELETE /attributevalue/{id}` |
|
| [`routes/attributevalue/_id.delete.md`](routes/attributevalue/_id.delete.md) | `DELETE /attributevalue/{id}` |
|
||||||
| [`routes/attributevalue/_id.patch.md`](routes/attributevalue/_id.patch.md) | `PATCH /attributevalue/{id}` |
|
| [`routes/attributevalue/_id.patch.md`](routes/attributevalue/_id.patch.md) | `PATCH /attributevalue/{id}` |
|
||||||
|
| [`routes/attributevalue/_id/image.delete.md`](routes/attributevalue/_id/image.delete.md) | `DELETE /attributevalue/{id}/image` |
|
||||||
|
| [`routes/attributevalue/_id/image.post.md`](routes/attributevalue/_id/image.post.md) | `POST /attributevalue/{id}/image` |
|
||||||
| [`routes/auth/login.md`](routes/auth/login.md) | `POST /auth/login` |
|
| [`routes/auth/login.md`](routes/auth/login.md) | `POST /auth/login` |
|
||||||
| [`routes/auth/logout.md`](routes/auth/logout.md) | `POST /auth/logout` |
|
| [`routes/auth/logout.md`](routes/auth/logout.md) | `POST /auth/logout` |
|
||||||
| [`routes/auth/me.md`](routes/auth/me.md) | `GET /auth/me` |
|
| [`routes/auth/me.md`](routes/auth/me.md) | `GET /auth/me` |
|
||||||
| [`routes/auth/selected-company.md`](routes/auth/selected-company.md) | `PUT /auth/selected-company` |
|
| [`routes/auth/selected-company.md`](routes/auth/selected-company.md) | `PUT /auth/selected-company` |
|
||||||
|
| [`routes/autoinvite/list.md`](routes/autoinvite/list.md) | `GET /autoinvite` |
|
||||||
|
| [`routes/autoinvite/create.md`](routes/autoinvite/create.md) | `POST /autoinvite` |
|
||||||
|
| [`routes/autoinvite/delete.md`](routes/autoinvite/delete.md) | `DELETE /autoinvite/{id}` |
|
||||||
|
| [`routes/backup.md`](routes/backup.md) | `GET /backup` |
|
||||||
|
| [`routes/backup/_id/vfs.md`](routes/backup/_id/vfs.md) | `GET /backup/{id}/vfs` |
|
||||||
|
| [`routes/backup/_id/vfs/recover.md`](routes/backup/_id/vfs/recover.md) | `POST /backup/{id}/vfs/recover` |
|
||||||
| [`routes/brand/list.md`](routes/brand/list.md) | `GET /brand` |
|
| [`routes/brand/list.md`](routes/brand/list.md) | `GET /brand` |
|
||||||
| [`routes/brand/upsert.md`](routes/brand/upsert.md) | `PUT /brand` |
|
| [`routes/brand/create.md`](routes/brand/create.md) | `POST /brand` |
|
||||||
| [`routes/brand/delete.md`](routes/brand/delete.md) | `DELETE /brand/{id}` |
|
| [`routes/brand/delete.md`](routes/brand/delete.md) | `DELETE /brand/{id}` |
|
||||||
|
| [`routes/brand/update.md`](routes/brand/update.md) | `PATCH /brand/{id}` |
|
||||||
| [`routes/brand/_id/image.delete.md`](routes/brand/_id/image.delete.md) | `DELETE /brand/{id}/image` |
|
| [`routes/brand/_id/image.delete.md`](routes/brand/_id/image.delete.md) | `DELETE /brand/{id}/image` |
|
||||||
| [`routes/brand/_id/image.post.md`](routes/brand/_id/image.post.md) | `POST /brand/{id}/image` |
|
| [`routes/brand/_id/image.post.md`](routes/brand/_id/image.post.md) | `POST /brand/{id}/image` |
|
||||||
|
| [`routes/carrier.get.md`](routes/carrier.get.md) | `GET /carrier` |
|
||||||
|
| [`routes/carrier.post.md`](routes/carrier.post.md) | `POST /carrier` |
|
||||||
|
| [`routes/carrier/order.md`](routes/carrier/order.md) | `PUT /carrier/order` |
|
||||||
|
| [`routes/carrier/_id.delete.md`](routes/carrier/_id.delete.md) | `DELETE /carrier/{id}` |
|
||||||
|
| [`routes/carrier/_id.get.md`](routes/carrier/_id.get.md) | `GET /carrier/{id}` |
|
||||||
|
| [`routes/carrier/_id.patch.md`](routes/carrier/_id.patch.md) | `PATCH /carrier/{id}` |
|
||||||
| [`routes/category/create.md`](routes/category/create.md) | `POST /category` |
|
| [`routes/category/create.md`](routes/category/create.md) | `POST /category` |
|
||||||
| [`routes/category/export.zip.md`](routes/category/export.zip.md) | `GET /category/export.zip` |
|
| [`routes/category/export.zip.md`](routes/category/export.zip.md) | `GET /category/export.zip` |
|
||||||
| [`routes/category/order.md`](routes/category/order.md) | `PUT /category/order` |
|
| [`routes/category/order.md`](routes/category/order.md) | `PUT /category/order` |
|
||||||
@@ -97,11 +127,14 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
|
|||||||
| [`routes/category/_id/attributevalue.put.md`](routes/category/_id/attributevalue.put.md) | `PUT /category/{id}/attributevalue` |
|
| [`routes/category/_id/attributevalue.put.md`](routes/category/_id/attributevalue.put.md) | `PUT /category/{id}/attributevalue` |
|
||||||
| [`routes/category/delete-image.md`](routes/category/delete-image.md) | `DELETE /category/{id}/image` |
|
| [`routes/category/delete-image.md`](routes/category/delete-image.md) | `DELETE /category/{id}/image` |
|
||||||
| [`routes/category/add-image.md`](routes/category/add-image.md) | `POST /category/{id}/image` |
|
| [`routes/category/add-image.md`](routes/category/add-image.md) | `POST /category/{id}/image` |
|
||||||
|
| [`routes/category/_id/image/generate.md`](routes/category/_id/image/generate.md) | `POST /category/{id}/image/generate` |
|
||||||
| [`routes/category/_id/property.get.md`](routes/category/_id/property.get.md) | `GET /category/{id}/property` |
|
| [`routes/category/_id/property.get.md`](routes/category/_id/property.get.md) | `GET /category/{id}/property` |
|
||||||
| [`routes/category/_id/property.put.md`](routes/category/_id/property.put.md) | `PUT /category/{id}/property` |
|
| [`routes/category/_id/property.put.md`](routes/category/_id/property.put.md) | `PUT /category/{id}/property` |
|
||||||
| [`routes/category/_id/property/_propertyId.md`](routes/category/_id/property/_propertyId.md) | `DELETE /category/{id}/property/{propertyId}` |
|
| [`routes/category/_id/property/_propertyId.md`](routes/category/_id/property/_propertyId.md) | `DELETE /category/{id}/property/{propertyId}` |
|
||||||
| [`routes/company/list.md`](routes/company/list.md) | `GET /company` |
|
| [`routes/company/list.md`](routes/company/list.md) | `GET /company` |
|
||||||
| [`routes/company/create.md`](routes/company/create.md) | `POST /company` |
|
| [`routes/company/create.md`](routes/company/create.md) | `POST /company` |
|
||||||
|
| [`routes/company/export.md`](routes/company/export.md) | `GET /company/export` |
|
||||||
|
| [`routes/company/export.post.md`](routes/company/export.post.md) | `POST /company/export` |
|
||||||
| [`routes/company/history.md`](routes/company/history.md) | `GET /company/history` |
|
| [`routes/company/history.md`](routes/company/history.md) | `GET /company/history` |
|
||||||
| [`routes/company/addHistory.md`](routes/company/addHistory.md) | `POST /company/history` |
|
| [`routes/company/addHistory.md`](routes/company/addHistory.md) | `POST /company/history` |
|
||||||
| [`routes/company/import.md`](routes/company/import.md) | `POST /company/import` |
|
| [`routes/company/import.md`](routes/company/import.md) | `POST /company/import` |
|
||||||
@@ -110,6 +143,10 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
|
|||||||
| [`routes/companysetting/set.md`](routes/companysetting/set.md) | `PUT /company/setting/{key}` |
|
| [`routes/companysetting/set.md`](routes/companysetting/set.md) | `PUT /company/setting/{key}` |
|
||||||
| [`routes/company/delete.md`](routes/company/delete.md) | `DELETE /company/{id}` |
|
| [`routes/company/delete.md`](routes/company/delete.md) | `DELETE /company/{id}` |
|
||||||
| [`routes/company/update.md`](routes/company/update.md) | `PUT /company/{id}` |
|
| [`routes/company/update.md`](routes/company/update.md) | `PUT /company/{id}` |
|
||||||
|
| [`routes/companyuser/list.md`](routes/companyuser/list.md) | `GET /companyuser` |
|
||||||
|
| [`routes/companyuser/create.md`](routes/companyuser/create.md) | `POST /companyuser` |
|
||||||
|
| [`routes/companyuser/delete.md`](routes/companyuser/delete.md) | `DELETE /companyuser/{id}` |
|
||||||
|
| [`routes/companyuser/update.md`](routes/companyuser/update.md) | `PATCH /companyuser/{id}` |
|
||||||
| [`routes/customergroup/list.md`](routes/customergroup/list.md) | `GET /customergroup` |
|
| [`routes/customergroup/list.md`](routes/customergroup/list.md) | `GET /customergroup` |
|
||||||
| [`routes/customergroup/create.md`](routes/customergroup/create.md) | `POST /customergroup` |
|
| [`routes/customergroup/create.md`](routes/customergroup/create.md) | `POST /customergroup` |
|
||||||
| [`routes/customergroup/order.md`](routes/customergroup/order.md) | `PUT /customergroup/order` |
|
| [`routes/customergroup/order.md`](routes/customergroup/order.md) | `PUT /customergroup/order` |
|
||||||
@@ -117,11 +154,12 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
|
|||||||
| [`routes/customergroup/update.md`](routes/customergroup/update.md) | `PATCH /customergroup/{id}` |
|
| [`routes/customergroup/update.md`](routes/customergroup/update.md) | `PATCH /customergroup/{id}` |
|
||||||
| [`routes/delivery/list.md`](routes/delivery/list.md) | `GET /delivery` |
|
| [`routes/delivery/list.md`](routes/delivery/list.md) | `GET /delivery` |
|
||||||
| [`routes/delivery/create.md`](routes/delivery/create.md) | `POST /delivery` |
|
| [`routes/delivery/create.md`](routes/delivery/create.md) | `POST /delivery` |
|
||||||
| [`routes/delivery/parse.md`](routes/delivery/parse.md) | `POST /delivery/parse` |
|
|
||||||
| [`routes/delivery/delete.md`](routes/delivery/delete.md) | `DELETE /delivery/{id}` |
|
| [`routes/delivery/delete.md`](routes/delivery/delete.md) | `DELETE /delivery/{id}` |
|
||||||
| [`routes/delivery/get.md`](routes/delivery/get.md) | `GET /delivery/{id}` |
|
| [`routes/delivery/get.md`](routes/delivery/get.md) | `GET /delivery/{id}` |
|
||||||
| [`routes/delivery/update.md`](routes/delivery/update.md) | `PATCH /delivery/{id}` |
|
| [`routes/delivery/update.md`](routes/delivery/update.md) | `PATCH /delivery/{id}` |
|
||||||
|
| [`routes/delivery/_id/file/_fileId.delete.md`](routes/delivery/_id/file/_fileId.delete.md) | `DELETE /delivery/{id}/file/{fileId}` |
|
||||||
| [`routes/delivery/file.md`](routes/delivery/file.md) | `GET /delivery/{id}/file/{fileId}` |
|
| [`routes/delivery/file.md`](routes/delivery/file.md) | `GET /delivery/{id}/file/{fileId}` |
|
||||||
|
| [`routes/delivery/_id/pdf.md`](routes/delivery/_id/pdf.md) | `POST /delivery/{id}/pdf` |
|
||||||
| [`routes/entity/list.md`](routes/entity/list.md) | `GET /entity` |
|
| [`routes/entity/list.md`](routes/entity/list.md) | `GET /entity` |
|
||||||
| [`routes/entity/create.md`](routes/entity/create.md) | `POST /entity` |
|
| [`routes/entity/create.md`](routes/entity/create.md) | `POST /entity` |
|
||||||
| [`routes/entity/next-number.md`](routes/entity/next-number.md) | `GET /entity/next-number` |
|
| [`routes/entity/next-number.md`](routes/entity/next-number.md) | `GET /entity/next-number` |
|
||||||
@@ -138,17 +176,26 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
|
|||||||
| [`routes/entity/update.md`](routes/entity/update.md) | `PATCH /entity/{id}` |
|
| [`routes/entity/update.md`](routes/entity/update.md) | `PATCH /entity/{id}` |
|
||||||
| [`routes/events.md`](routes/events.md) | `GET /events` |
|
| [`routes/events.md`](routes/events.md) | `GET /events` |
|
||||||
| [`routes/health.md`](routes/health.md) | `GET /health` |
|
| [`routes/health.md`](routes/health.md) | `GET /health` |
|
||||||
|
| [`routes/health/latency.md`](routes/health/latency.md) | `GET /health/latency` |
|
||||||
| [`routes/image/get.md`](routes/image/get.md) | `GET /image/{fullHash}` |
|
| [`routes/image/get.md`](routes/image/get.md) | `GET /image/{fullHash}` |
|
||||||
| [`routes/labeltemplate/list.md`](routes/labeltemplate/list.md) | `GET /labeltemplate` |
|
| [`routes/labeltemplate/list.md`](routes/labeltemplate/list.md) | `GET /labeltemplate` |
|
||||||
| [`routes/labeltemplate/create.md`](routes/labeltemplate/create.md) | `POST /labeltemplate` |
|
| [`routes/labeltemplate/create.md`](routes/labeltemplate/create.md) | `POST /labeltemplate` |
|
||||||
|
| [`routes/labeltemplate/testprint.md`](routes/labeltemplate/testprint.md) | `POST /labeltemplate/testprint` |
|
||||||
| [`routes/labeltemplate/delete.md`](routes/labeltemplate/delete.md) | `DELETE /labeltemplate/{id}` |
|
| [`routes/labeltemplate/delete.md`](routes/labeltemplate/delete.md) | `DELETE /labeltemplate/{id}` |
|
||||||
| [`routes/labeltemplate/get.md`](routes/labeltemplate/get.md) | `GET /labeltemplate/{id}` |
|
| [`routes/labeltemplate/get.md`](routes/labeltemplate/get.md) | `GET /labeltemplate/{id}` |
|
||||||
| [`routes/labeltemplate/update.md`](routes/labeltemplate/update.md) | `PATCH /labeltemplate/{id}` |
|
| [`routes/labeltemplate/update.md`](routes/labeltemplate/update.md) | `PATCH /labeltemplate/{id}` |
|
||||||
| [`routes/labeltemplate/update.md`](routes/labeltemplate/update.md) | `PUT /labeltemplate/{id}` |
|
| [`routes/labeltemplate/update.md`](routes/labeltemplate/update.md) | `PUT /labeltemplate/{id}` |
|
||||||
| [`routes/ledger.get.md`](routes/ledger.get.md) | `GET /ledger` |
|
| [`routes/ledger.md`](routes/ledger.md) | `GET /ledger` |
|
||||||
| [`routes/ledger.post.md`](routes/ledger.post.md) | `POST /ledger` |
|
| [`routes/ledger/history.md`](routes/ledger/history.md) | `GET /ledger/history` |
|
||||||
| [`routes/ledger/manifestarticle/_manifestarticleId.md`](routes/ledger/manifestarticle/_manifestarticleId.md) | `GET /ledger/manifestarticle/{manifestarticleId}` |
|
| [`routes/ledger/manifestarticle/_manifestarticleId.md`](routes/ledger/manifestarticle/_manifestarticleId.md) | `GET /ledger/manifestarticle/{manifestarticleId}` |
|
||||||
| [`routes/ledger/sku/_sku.md`](routes/ledger/sku/_sku.md) | `GET /ledger/sku/{sku}` |
|
| [`routes/ledger/sku/_sku.md`](routes/ledger/sku/_sku.md) | `GET /ledger/sku/{sku}` |
|
||||||
|
| [`routes/mcp.md`](routes/mcp.md) | `POST /mcp` |
|
||||||
|
| [`routes/mcp/authorizations.md`](routes/mcp/authorizations.md) | `GET /mcp/authorizations` |
|
||||||
|
| [`routes/mcp/authorizations/_clientId.md`](routes/mcp/authorizations/_clientId.md) | `DELETE /mcp/authorizations/{clientId}` |
|
||||||
|
| [`routes/mcp/authorize.get.md`](routes/mcp/authorize.get.md) | `GET /mcp/authorize` |
|
||||||
|
| [`routes/mcp/authorize.post.md`](routes/mcp/authorize.post.md) | `POST /mcp/authorize` |
|
||||||
|
| [`routes/mcp/clients.md`](routes/mcp/clients.md) | `GET /mcp/clients` |
|
||||||
|
| [`routes/mcp/clients/_clientId.md`](routes/mcp/clients/_clientId.md) | `DELETE /mcp/clients/{clientId}` |
|
||||||
| [`routes/modelconversation.get.md`](routes/modelconversation.get.md) | `GET /modelconversation` |
|
| [`routes/modelconversation.get.md`](routes/modelconversation.get.md) | `GET /modelconversation` |
|
||||||
| [`routes/modelconversation.post.md`](routes/modelconversation.post.md) | `POST /modelconversation` |
|
| [`routes/modelconversation.post.md`](routes/modelconversation.post.md) | `POST /modelconversation` |
|
||||||
| [`routes/modelconversation/_id.delete.md`](routes/modelconversation/_id.delete.md) | `DELETE /modelconversation/{id}` |
|
| [`routes/modelconversation/_id.delete.md`](routes/modelconversation/_id.delete.md) | `DELETE /modelconversation/{id}` |
|
||||||
@@ -158,11 +205,26 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
|
|||||||
| [`routes/modelconversation/_id/stream.md`](routes/modelconversation/_id/stream.md) | `GET /modelconversation/{id}/stream` |
|
| [`routes/modelconversation/_id/stream.md`](routes/modelconversation/_id/stream.md) | `GET /modelconversation/{id}/stream` |
|
||||||
| [`routes/number/list.md`](routes/number/list.md) | `GET /number` |
|
| [`routes/number/list.md`](routes/number/list.md) | `GET /number` |
|
||||||
| [`routes/number/update.md`](routes/number/update.md) | `PATCH /number/{id}` |
|
| [`routes/number/update.md`](routes/number/update.md) | `PATCH /number/{id}` |
|
||||||
|
| [`routes/oauth/authorize.md`](routes/oauth/authorize.md) | `GET /oauth/authorize` |
|
||||||
|
| [`routes/oauth/introspect.md`](routes/oauth/introspect.md) | `POST /oauth/introspect` |
|
||||||
|
| [`routes/oauth/register.md`](routes/oauth/register.md) | `POST /oauth/register` |
|
||||||
|
| [`routes/oauth/token.md`](routes/oauth/token.md) | `POST /oauth/token` |
|
||||||
| [`routes/order/list.md`](routes/order/list.md) | `GET /order` |
|
| [`routes/order/list.md`](routes/order/list.md) | `GET /order` |
|
||||||
| [`routes/order/create.md`](routes/order/create.md) | `POST /order` |
|
| [`routes/order/create.md`](routes/order/create.md) | `POST /order` |
|
||||||
| [`routes/order/delete.md`](routes/order/delete.md) | `DELETE /order/{id}` |
|
| [`routes/order/delete.md`](routes/order/delete.md) | `DELETE /order/{id}` |
|
||||||
| [`routes/order/get.md`](routes/order/get.md) | `GET /order/{id}` |
|
| [`routes/order/get.md`](routes/order/get.md) | `GET /order/{id}` |
|
||||||
| [`routes/order/update.md`](routes/order/update.md) | `PATCH /order/{id}` |
|
| [`routes/order/update.md`](routes/order/update.md) | `PATCH /order/{id}` |
|
||||||
|
| [`routes/order/_id/file/_fileId.delete.md`](routes/order/_id/file/_fileId.delete.md) | `DELETE /order/{id}/file/{fileId}` |
|
||||||
|
| [`routes/order/_id/file/_fileId.get.md`](routes/order/_id/file/_fileId.get.md) | `GET /order/{id}/file/{fileId}` |
|
||||||
|
| [`routes/order/_id/payment.get.md`](routes/order/_id/payment.get.md) | `GET /order/{id}/payment` |
|
||||||
|
| [`routes/order/_id/payment.post.md`](routes/order/_id/payment.post.md) | `POST /order/{id}/payment` |
|
||||||
|
| [`routes/order/_id/pdf.md`](routes/order/_id/pdf.md) | `POST /order/{id}/pdf` |
|
||||||
|
| [`routes/order/_id/shipment.get.md`](routes/order/_id/shipment.get.md) | `GET /order/{id}/shipment` |
|
||||||
|
| [`routes/order/_id/shipment.post.md`](routes/order/_id/shipment.post.md) | `POST /order/{id}/shipment` |
|
||||||
|
| [`routes/order/_id/shipping.get.md`](routes/order/_id/shipping.get.md) | `GET /order/{id}/shipping` |
|
||||||
|
| [`routes/order/_id/shipping.post.md`](routes/order/_id/shipping.post.md) | `POST /order/{id}/shipping` |
|
||||||
|
| [`routes/package/_id.md`](routes/package/_id.md) | `DELETE /package/{id}` |
|
||||||
|
| [`routes/payment/_id.md`](routes/payment/_id.md) | `DELETE /payment/{id}` |
|
||||||
| [`routes/posterminal/list.md`](routes/posterminal/list.md) | `GET /posterminal` |
|
| [`routes/posterminal/list.md`](routes/posterminal/list.md) | `GET /posterminal` |
|
||||||
| [`routes/posterminal/upsert.md`](routes/posterminal/upsert.md) | `PUT /posterminal` |
|
| [`routes/posterminal/upsert.md`](routes/posterminal/upsert.md) | `PUT /posterminal` |
|
||||||
| [`routes/posterminal/delete.md`](routes/posterminal/delete.md) | `DELETE /posterminal/{id}` |
|
| [`routes/posterminal/delete.md`](routes/posterminal/delete.md) | `DELETE /posterminal/{id}` |
|
||||||
@@ -174,15 +236,28 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
|
|||||||
| [`routes/printer/list.md`](routes/printer/list.md) | `GET /printer` |
|
| [`routes/printer/list.md`](routes/printer/list.md) | `GET /printer` |
|
||||||
| [`routes/printer/create.md`](routes/printer/create.md) | `POST /printer` |
|
| [`routes/printer/create.md`](routes/printer/create.md) | `POST /printer` |
|
||||||
| [`routes/printer/order.md`](routes/printer/order.md) | `PUT /printer/order` |
|
| [`routes/printer/order.md`](routes/printer/order.md) | `PUT /printer/order` |
|
||||||
|
| [`routes/printer/state.md`](routes/printer/state.md) | `GET /printer/state` |
|
||||||
| [`routes/printer/delete.md`](routes/printer/delete.md) | `DELETE /printer/{id}` |
|
| [`routes/printer/delete.md`](routes/printer/delete.md) | `DELETE /printer/{id}` |
|
||||||
| [`routes/printer/get.md`](routes/printer/get.md) | `GET /printer/{id}` |
|
| [`routes/printer/get.md`](routes/printer/get.md) | `GET /printer/{id}` |
|
||||||
| [`routes/printer/update.md`](routes/printer/update.md) | `PATCH /printer/{id}` |
|
| [`routes/printer/update.md`](routes/printer/update.md) | `PATCH /printer/{id}` |
|
||||||
| [`routes/printer/_id.put.md`](routes/printer/_id.put.md) | `PUT /printer/{id}` |
|
| [`routes/printer/_id.put.md`](routes/printer/_id.put.md) | `PUT /printer/{id}` |
|
||||||
|
| [`routes/printer/_id/state.md`](routes/printer/_id/state.md) | `GET /printer/{id}/state` |
|
||||||
|
| [`routes/printeragent.get.md`](routes/printeragent.get.md) | `GET /printeragent` |
|
||||||
|
| [`routes/printeragent.put.md`](routes/printeragent.put.md) | `PUT /printeragent` |
|
||||||
|
| [`routes/printeragent/_id.delete.md`](routes/printeragent/_id.delete.md) | `DELETE /printeragent/{id}` |
|
||||||
|
| [`routes/printeragent/_id.patch.md`](routes/printeragent/_id.patch.md) | `PATCH /printeragent/{id}` |
|
||||||
|
| [`routes/printerjob.get.md`](routes/printerjob.get.md) | `GET /printerjob` |
|
||||||
|
| [`routes/printerjob.post.md`](routes/printerjob.post.md) | `POST /printerjob` |
|
||||||
|
| [`routes/printerjob/cancel-all.md`](routes/printerjob/cancel-all.md) | `POST /printerjob/cancel-all` |
|
||||||
|
| [`routes/printerjob/_id.delete.md`](routes/printerjob/_id.delete.md) | `DELETE /printerjob/{id}` |
|
||||||
|
| [`routes/printerjob/_id.get.md`](routes/printerjob/_id.get.md) | `GET /printerjob/{id}` |
|
||||||
|
| [`routes/printerjob/_id/cancel.md`](routes/printerjob/_id/cancel.md) | `POST /printerjob/{id}/cancel` |
|
||||||
| [`routes/propertyname.get.md`](routes/propertyname.get.md) | `GET /propertyname` |
|
| [`routes/propertyname.get.md`](routes/propertyname.get.md) | `GET /propertyname` |
|
||||||
| [`routes/propertyname.post.md`](routes/propertyname.post.md) | `POST /propertyname` |
|
| [`routes/propertyname.post.md`](routes/propertyname.post.md) | `POST /propertyname` |
|
||||||
| [`routes/propertyname/order.md`](routes/propertyname/order.md) | `PUT /propertyname/order` |
|
| [`routes/propertyname/order.md`](routes/propertyname/order.md) | `PUT /propertyname/order` |
|
||||||
| [`routes/propertyname/_id.delete.md`](routes/propertyname/_id.delete.md) | `DELETE /propertyname/{id}` |
|
| [`routes/propertyname/_id.delete.md`](routes/propertyname/_id.delete.md) | `DELETE /propertyname/{id}` |
|
||||||
| [`routes/propertyname/_id.patch.md`](routes/propertyname/_id.patch.md) | `PATCH /propertyname/{id}` |
|
| [`routes/propertyname/_id.patch.md`](routes/propertyname/_id.patch.md) | `PATCH /propertyname/{id}` |
|
||||||
|
| [`routes/public/telemetry/crash.md`](routes/public/telemetry/crash.md) | `POST /public/telemetry/crash` |
|
||||||
| [`routes/public/_companyId/articles.md`](routes/public/_companyId/articles.md) | `GET /public/{companyId}/articles` |
|
| [`routes/public/_companyId/articles.md`](routes/public/_companyId/articles.md) | `GET /public/{companyId}/articles` |
|
||||||
| [`routes/public/_companyId/articles/_sku.md`](routes/public/_companyId/articles/_sku.md) | `GET /public/{companyId}/articles/{sku}` |
|
| [`routes/public/_companyId/articles/_sku.md`](routes/public/_companyId/articles/_sku.md) | `GET /public/{companyId}/articles/{sku}` |
|
||||||
| [`routes/public/_companyId/categories.md`](routes/public/_companyId/categories.md) | `GET /public/{companyId}/categories` |
|
| [`routes/public/_companyId/categories.md`](routes/public/_companyId/categories.md) | `GET /public/{companyId}/categories` |
|
||||||
@@ -191,9 +266,48 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
|
|||||||
| [`routes/quote/delete.md`](routes/quote/delete.md) | `DELETE /quote/{id}` |
|
| [`routes/quote/delete.md`](routes/quote/delete.md) | `DELETE /quote/{id}` |
|
||||||
| [`routes/quote/get.md`](routes/quote/get.md) | `GET /quote/{id}` |
|
| [`routes/quote/get.md`](routes/quote/get.md) | `GET /quote/{id}` |
|
||||||
| [`routes/quote/update.md`](routes/quote/update.md) | `PATCH /quote/{id}` |
|
| [`routes/quote/update.md`](routes/quote/update.md) | `PATCH /quote/{id}` |
|
||||||
|
| [`routes/quote/_id/file/_fileId.delete.md`](routes/quote/_id/file/_fileId.delete.md) | `DELETE /quote/{id}/file/{fileId}` |
|
||||||
|
| [`routes/quote/_id/file/_fileId.get.md`](routes/quote/_id/file/_fileId.get.md) | `GET /quote/{id}/file/{fileId}` |
|
||||||
|
| [`routes/quote/order.md`](routes/quote/order.md) | `POST /quote/{id}/order` |
|
||||||
|
| [`routes/quote/_id/pdf.md`](routes/quote/_id/pdf.md) | `POST /quote/{id}/pdf` |
|
||||||
|
| [`routes/retoure.get.md`](routes/retoure.get.md) | `GET /retoure` |
|
||||||
|
| [`routes/retoure.post.md`](routes/retoure.post.md) | `POST /retoure` |
|
||||||
|
| [`routes/retoure/rebuild-summaries.md`](routes/retoure/rebuild-summaries.md) | `POST /retoure/rebuild-summaries` |
|
||||||
|
| [`routes/retoure/_id.delete.md`](routes/retoure/_id.delete.md) | `DELETE /retoure/{id}` |
|
||||||
|
| [`routes/retoure/_id.get.md`](routes/retoure/_id.get.md) | `GET /retoure/{id}` |
|
||||||
|
| [`routes/retoure/_id.put.md`](routes/retoure/_id.put.md) | `PUT /retoure/{id}` |
|
||||||
|
| [`routes/retoure/_id/shipment.md`](routes/retoure/_id/shipment.md) | `POST /retoure/{id}/shipment` |
|
||||||
|
| [`routes/return.get.md`](routes/return.get.md) | `GET /return` |
|
||||||
|
| [`routes/return.post.md`](routes/return.post.md) | `POST /return` |
|
||||||
|
| [`routes/return/rebuild-summaries.md`](routes/return/rebuild-summaries.md) | `POST /return/rebuild-summaries` |
|
||||||
|
| [`routes/return/_id.delete.md`](routes/return/_id.delete.md) | `DELETE /return/{id}` |
|
||||||
|
| [`routes/return/_id.get.md`](routes/return/_id.get.md) | `GET /return/{id}` |
|
||||||
|
| [`routes/return/_id.put.md`](routes/return/_id.put.md) | `PUT /return/{id}` |
|
||||||
|
| [`routes/return/_id/book.md`](routes/return/_id/book.md) | `POST /return/{id}/book` |
|
||||||
| [`routes/serper/image.md`](routes/serper/image.md) | `POST /serper/image` |
|
| [`routes/serper/image.md`](routes/serper/image.md) | `POST /serper/image` |
|
||||||
| [`routes/serper/images.md`](routes/serper/images.md) | `POST /serper/images` |
|
| [`routes/serper/images.md`](routes/serper/images.md) | `POST /serper/images` |
|
||||||
|
| [`routes/shipment.md`](routes/shipment.md) | `GET /shipment` |
|
||||||
|
| [`routes/shipment/_id.delete.md`](routes/shipment/_id.delete.md) | `DELETE /shipment/{id}` |
|
||||||
|
| [`routes/shipment/_id.get.md`](routes/shipment/_id.get.md) | `GET /shipment/{id}` |
|
||||||
|
| [`routes/shipment/_id.patch.md`](routes/shipment/_id.patch.md) | `PATCH /shipment/{id}` |
|
||||||
|
| [`routes/shipment/_id/file/_fileId.delete.md`](routes/shipment/_id/file/_fileId.delete.md) | `DELETE /shipment/{id}/file/{fileId}` |
|
||||||
|
| [`routes/shipment/_id/file/_fileId.get.md`](routes/shipment/_id/file/_fileId.get.md) | `GET /shipment/{id}/file/{fileId}` |
|
||||||
|
| [`routes/shipment/_id/pdf.md`](routes/shipment/_id/pdf.md) | `POST /shipment/{id}/pdf` |
|
||||||
|
| [`routes/shipment/_id/shipping.get.md`](routes/shipment/_id/shipping.get.md) | `GET /shipment/{id}/shipping` |
|
||||||
|
| [`routes/shipment/_id/shipping.post.md`](routes/shipment/_id/shipping.post.md) | `POST /shipment/{id}/shipping` |
|
||||||
|
| [`routes/shipping/_id.delete.md`](routes/shipping/_id.delete.md) | `DELETE /shipping/{id}` |
|
||||||
|
| [`routes/shipping/_id.patch.md`](routes/shipping/_id.patch.md) | `PATCH /shipping/{id}` |
|
||||||
|
| [`routes/shipping/_id/package.get.md`](routes/shipping/_id/package.get.md) | `GET /shipping/{id}/package` |
|
||||||
|
| [`routes/shipping/_id/package.post.md`](routes/shipping/_id/package.post.md) | `POST /shipping/{id}/package` |
|
||||||
|
| [`routes/smallthumb/get.md`](routes/smallthumb/get.md) | `GET /smallthumb/{fullHash}` |
|
||||||
|
| [`routes/stock/accept.md`](routes/stock/accept.md) | `POST /stock/accept` |
|
||||||
| [`routes/stock/adjust.md`](routes/stock/adjust.md) | `POST /stock/adjust` |
|
| [`routes/stock/adjust.md`](routes/stock/adjust.md) | `POST /stock/adjust` |
|
||||||
|
| [`routes/stock/ship.md`](routes/stock/ship.md) | `POST /stock/ship` |
|
||||||
|
| [`routes/subagentschedule.get.md`](routes/subagentschedule.get.md) | `GET /subagentschedule` |
|
||||||
|
| [`routes/subagentschedule.post.md`](routes/subagentschedule.post.md) | `POST /subagentschedule` |
|
||||||
|
| [`routes/subagentschedule/_id.delete.md`](routes/subagentschedule/_id.delete.md) | `DELETE /subagentschedule/{id}` |
|
||||||
|
| [`routes/subagentschedule/_id.get.md`](routes/subagentschedule/_id.get.md) | `GET /subagentschedule/{id}` |
|
||||||
|
| [`routes/subagentschedule/_id.put.md`](routes/subagentschedule/_id.put.md) | `PUT /subagentschedule/{id}` |
|
||||||
| [`routes/supplier/list.md`](routes/supplier/list.md) | `GET /supplier` |
|
| [`routes/supplier/list.md`](routes/supplier/list.md) | `GET /supplier` |
|
||||||
| [`routes/supplier/create.md`](routes/supplier/create.md) | `POST /supplier` |
|
| [`routes/supplier/create.md`](routes/supplier/create.md) | `POST /supplier` |
|
||||||
| [`routes/supplier/delete.md`](routes/supplier/delete.md) | `DELETE /supplier/{id}` |
|
| [`routes/supplier/delete.md`](routes/supplier/delete.md) | `DELETE /supplier/{id}` |
|
||||||
@@ -205,16 +319,24 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
|
|||||||
| [`routes/unit/list.md`](routes/unit/list.md) | `GET /unit` |
|
| [`routes/unit/list.md`](routes/unit/list.md) | `GET /unit` |
|
||||||
| [`routes/unit/upsert.md`](routes/unit/upsert.md) | `PUT /unit` |
|
| [`routes/unit/upsert.md`](routes/unit/upsert.md) | `PUT /unit` |
|
||||||
| [`routes/v1/category.md`](routes/v1/category.md) | `GET /v1/category` |
|
| [`routes/v1/category.md`](routes/v1/category.md) | `GET /v1/category` |
|
||||||
|
| [`routes/pos/cimage.md`](routes/pos/cimage.md) | `GET /v1/cimage` |
|
||||||
| [`routes/pos/client.md`](routes/pos/client.md) | `GET /v1/client` |
|
| [`routes/pos/client.md`](routes/pos/client.md) | `GET /v1/client` |
|
||||||
|
| [`routes/v1/customer.md`](routes/v1/customer.md) | `GET /v1/customer` |
|
||||||
|
| [`routes/v1/customergroup.md`](routes/v1/customergroup.md) | `GET /v1/customergroup` |
|
||||||
|
| [`routes/v1/deletedentity.md`](routes/v1/deletedentity.md) | `GET /v1/deletedentity` |
|
||||||
| [`routes/pos/init.md`](routes/pos/init.md) | `GET /v1/init` |
|
| [`routes/pos/init.md`](routes/pos/init.md) | `GET /v1/init` |
|
||||||
|
| [`routes/v1/order.md`](routes/v1/order.md) | `POST /v1/order` |
|
||||||
| [`routes/pos/pimage.md`](routes/pos/pimage.md) | `GET /v1/pimage` |
|
| [`routes/pos/pimage.md`](routes/pos/pimage.md) | `GET /v1/pimage` |
|
||||||
| [`routes/v1/product.md`](routes/v1/product.md) | `GET /v1/product` |
|
| [`routes/v1/product.md`](routes/v1/product.md) | `GET /v1/product` |
|
||||||
| [`routes/vfs/list.md`](routes/vfs/list.md) | `GET /vfs` |
|
| [`routes/vfs/list.md`](routes/vfs/list.md) | `GET /vfs` |
|
||||||
|
| [`routes/vfs/applets-restore.md`](routes/vfs/applets-restore.md) | `POST /vfs/applets/restore` |
|
||||||
| [`routes/vfs/mkdir.md`](routes/vfs/mkdir.md) | `POST /vfs/mkdir` |
|
| [`routes/vfs/mkdir.md`](routes/vfs/mkdir.md) | `POST /vfs/mkdir` |
|
||||||
| [`routes/vfs/rename.md`](routes/vfs/rename.md) | `POST /vfs/rename` |
|
| [`routes/vfs/rename.md`](routes/vfs/rename.md) | `POST /vfs/rename` |
|
||||||
|
| [`routes/vfs/skills-restore.md`](routes/vfs/skills-restore.md) | `POST /vfs/skills/restore` |
|
||||||
| [`routes/vfs/upload.md`](routes/vfs/upload.md) | `POST /vfs/upload` |
|
| [`routes/vfs/upload.md`](routes/vfs/upload.md) | `POST /vfs/upload` |
|
||||||
| [`routes/vfs/delete.md`](routes/vfs/delete.md) | `DELETE /vfs/{*path}` |
|
| [`routes/vfs/delete.md`](routes/vfs/delete.md) | `DELETE /vfs/{*path}` |
|
||||||
| [`routes/vfs/_*path.md`](routes/vfs/_*path.md) | `GET /vfs/{*path}` |
|
| [`routes/vfs/_*path.md`](routes/vfs/_*path.md) | `GET /vfs/{*path}` |
|
||||||
|
| [`routes/vfs/exec.md`](routes/vfs/exec.md) | `POST /vfs/{*path}` |
|
||||||
| [`routes/vfs/write.md`](routes/vfs/write.md) | `PUT /vfs/{*path}` |
|
| [`routes/vfs/write.md`](routes/vfs/write.md) | `PUT /vfs/{*path}` |
|
||||||
| [`routes/warehouse/list.md`](routes/warehouse/list.md) | `GET /warehouse` |
|
| [`routes/warehouse/list.md`](routes/warehouse/list.md) | `GET /warehouse` |
|
||||||
| [`routes/warehouse/create.md`](routes/warehouse/create.md) | `POST /warehouse` |
|
| [`routes/warehouse/create.md`](routes/warehouse/create.md) | `POST /warehouse` |
|
||||||
@@ -223,6 +345,7 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
|
|||||||
| [`routes/warehouse/get.md`](routes/warehouse/get.md) | `GET /warehouse/{id}` |
|
| [`routes/warehouse/get.md`](routes/warehouse/get.md) | `GET /warehouse/{id}` |
|
||||||
| [`routes/warehouse/update.md`](routes/warehouse/update.md) | `PATCH /warehouse/{id}` |
|
| [`routes/warehouse/update.md`](routes/warehouse/update.md) | `PATCH /warehouse/{id}` |
|
||||||
| [`routes/warehouse/_id.put.md`](routes/warehouse/_id.put.md) | `PUT /warehouse/{id}` |
|
| [`routes/warehouse/_id.put.md`](routes/warehouse/_id.put.md) | `PUT /warehouse/{id}` |
|
||||||
|
| [`routes/zpljob.md`](routes/zpljob.md) | `POST /zpljob` |
|
||||||
|
|
||||||
Route overview: [`routes.md`](routes.md) | All routes: [`routes-all.md`](routes-all.md).
|
Route overview: [`routes.md`](routes.md) | All routes: [`routes-all.md`](routes-all.md).
|
||||||
|
|
||||||
@@ -232,10 +355,33 @@ Clients subscribe via [`GET /events`](routes/events.md) (`text/event-stream`). A
|
|||||||
|
|
||||||
| Signal | Emitted by |
|
| Signal | Emitted by |
|
||||||
|--------|------------|
|
|--------|------------|
|
||||||
| `article-<sku>` | `PUT /article/{sku}/brand`, `PUT /article/{sku}/categories`, `PUT /article/{sku}/eans`, `POST /article/{sku}/image`, `DELETE /article/{sku}/image/{fullHash}`, `PUT /article/{sku}/images/order`, `PUT /article/{sku}/prices`, `POST /article/{sku}/supplier`, `PUT /article/{sku}/supplier/order`, `DELETE /article/{sku}/supplier/{id}`, `PATCH /article/{sku}/supplier/{id}`, `PUT /article/{sku}/track_stock`, `PUT /article/{sku}/weight` |
|
| `agentprinter` | `PUT /agentprinter/order`, `DELETE /agentprinter/{id}`, `PATCH /agentprinter/{id}` |
|
||||||
| `articles` | `POST /article`, `DELETE /article/{sku}`, `PATCH /article/{sku}` |
|
| `article-<sku>` | `PUT /article/{sku}/brand`, `PUT /article/{sku}/categories`, `PUT /article/{sku}/eans`, `POST /article/{sku}/image`, `DELETE /article/{sku}/image/{fullHash}`, `PUT /article/{sku}/images/order`, `PUT /article/{sku}/prices`, `POST /article/{sku}/supplier`, `PUT /article/{sku}/supplier/order`, `DELETE /article/{sku}/supplier/{id}`, `PATCH /article/{sku}/supplier/{id}`, `PUT /article/{sku}/track_stock`, `PUT /article/{sku}/weight`, `POST /stock/adjust` |
|
||||||
| `category` | `POST /category`, `PUT /category/order`, `DELETE /category/{id}`, `PATCH /category/{id}`, `DELETE /category/{id}/image`, `POST /category/{id}/image` |
|
| `articles` | `POST /article`, `DELETE /article/{sku}`, `PATCH /article/{sku}`, `DELETE /category/{id}`, `DELETE /shipment/{id}`, `POST /stock/accept`, `POST /stock/adjust`, `POST /stock/ship` |
|
||||||
| `vfs` | `POST /vfs/mkdir`, `POST /vfs/rename`, `POST /vfs/upload`, `DELETE /vfs/{*path}`, `PUT /vfs/{*path}` |
|
| `attributename` | `POST /attributename`, `PUT /attributename/order`, `DELETE /attributename/{id}`, `PATCH /attributename/{id}`, `DELETE /attributevalue/{id}/image`, `POST /attributevalue/{id}/image` |
|
||||||
|
| `autoinvite` | `POST /autoinvite`, `DELETE /autoinvite/{id}` |
|
||||||
|
| `brand` | `POST /brand`, `DELETE /brand/{id}`, `PATCH /brand/{id}`, `DELETE /brand/{id}/image`, `POST /brand/{id}/image` |
|
||||||
|
| `carrier` | `POST /carrier`, `PUT /carrier/order`, `DELETE /carrier/{id}`, `PATCH /carrier/{id}` |
|
||||||
|
| `category` | `POST /category`, `PUT /category/order`, `DELETE /category/{id}`, `PATCH /category/{id}`, `DELETE /category/{id}/image`, `POST /category/{id}/image`, `POST /category/{id}/image/generate` |
|
||||||
|
| `company` | `PUT /auth/selected-company`, `POST /company`, `DELETE /company/{id}`, `PUT /company/{id}` |
|
||||||
|
| `companyuser` | `POST /companyuser`, `DELETE /companyuser/{id}`, `PATCH /companyuser/{id}` |
|
||||||
|
| `delivery` | `DELETE /delivery/{id}/file/{fileId}`, `POST /delivery/{id}/pdf`, `POST /stock/accept` |
|
||||||
|
| `labeltemplate` | `POST /labeltemplate`, `DELETE /labeltemplate/{id}`, `PATCH /labeltemplate/{id}`, `PUT /labeltemplate/{id}` |
|
||||||
|
| `ledger` | `DELETE /shipment/{id}`, `POST /stock/accept`, `POST /stock/adjust`, `POST /stock/ship` |
|
||||||
|
| `modelconversation` | `POST /modelconversation`, `DELETE /modelconversation/{id}` |
|
||||||
|
| `modelconversation-<id>` | `POST /modelconversation/{id}/message` |
|
||||||
|
| `order` | `DELETE /order/{id}/file/{fileId}`, `POST /order/{id}/pdf`, `DELETE /shipment/{id}`, `PATCH /shipment/{id}`, `DELETE /shipment/{id}/file/{fileId}` |
|
||||||
|
| `printer` | `POST /printer`, `PUT /printer/order`, `DELETE /printer/{id}`, `PATCH /printer/{id}`, `PUT /printer/{id}` |
|
||||||
|
| `propertyname` | `POST /propertyname`, `PUT /propertyname/order`, `DELETE /propertyname/{id}`, `PATCH /propertyname/{id}` |
|
||||||
|
| `quote` | `DELETE /quote/{id}/file/{fileId}`, `POST /quote/{id}/pdf` |
|
||||||
|
| `shipment` | `DELETE /shipment/{id}`, `PATCH /shipment/{id}`, `DELETE /shipment/{id}/file/{fileId}`, `POST /shipment/{id}/pdf` |
|
||||||
|
| `shipping` | `POST /stock/ship` |
|
||||||
|
| `stock` | `DELETE /shipment/{id}`, `POST /stock/accept`, `POST /stock/adjust`, `POST /stock/ship` |
|
||||||
|
| `subagentschedule` | `POST /subagentschedule`, `DELETE /subagentschedule/{id}`, `PUT /subagentschedule/{id}` |
|
||||||
|
| `supplier` | `POST /supplier`, `DELETE /supplier/{id}`, `PATCH /supplier/{id}` |
|
||||||
|
| `user` | `PUT /auth/selected-company`, `POST /company`, `DELETE /company/{id}` |
|
||||||
|
| `vfs` | `POST /vfs/applets/restore`, `POST /vfs/mkdir`, `POST /vfs/rename`, `POST /vfs/skills/restore`, `POST /vfs/upload`, `DELETE /vfs/{*path}`, `POST /vfs/{*path}`, `PUT /vfs/{*path}` |
|
||||||
|
| `warehouse` | `POST /warehouse`, `PUT /warehouse/order`, `DELETE /warehouse/{id}`, `PUT /warehouse/{id}` |
|
||||||
|
|
||||||
## DESIGN
|
## DESIGN
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
# Dash API Routes Overview
|
# Dash API Routes Overview
|
||||||
|
|
||||||
Overview of all 146 API endpoints across all modules.
|
Overview of all 196 API endpoints across all modules.
|
||||||
|
|
||||||
### Notation
|
### Notation
|
||||||
|
|
||||||
@@ -9,61 +9,54 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body).
|
|||||||
|
|
||||||
### Reusable types
|
### Reusable types
|
||||||
|
|
||||||
- `T1` = `{ article: T2 }`
|
- `T1` = `{ id: int, number: str, type: str, entityId: int, date: str, lines: T11[], externalNumber?: str|null, createdAt?: str, shipping?: T13|null, shippings?: T13[], payment?: T21|null, payments?: T21[], paid?: bool, shipped?: bool, delivered?: bool, orderTotal?: num, paidTotal?: num, orderedQuantity?: num, shippedQuantity?: num, shipments?: T2[], files?: T7[] }`
|
||||||
- `T2` = `{ id: int, sku: str, name: str, slug: str, desc: str|null, seo: str|null, track_stock: bool, unitamount: num|null, weight: num|null, unit: T30, brand: T27|null, categoryIds: int[], photos: T25[], eans: str[], prices: T32[], suppliers: T11[] }`
|
- `T2` = `{ id: int, number: str, type: str, date: str, lines: T11[], shippings: T13[], entityId?: int|null, externalNumber?: str|null, createdAt?: str|null, lineCount?: int, packageCount?: int, files?: T7[], state?: str }`
|
||||||
- `T3` = `{ delivery: T4 }`
|
- `T3` = `{ id: int, number: str, type: str, date: str, lines: T11[], entityId?: int|null, externalNumber?: str|null, createdAt?: str, positions?: int, grossTotal?: num, totalQuantity?: num, customerName?: str|null, files?: T7[] }`
|
||||||
- `T4` = `{ id: int, number: str, type: str, supplierId: int|null, date: str, lines: T19[], files: T9[], externalNumber?: str|null, createdAt?: str, ledger?: T36 }`
|
- `T4` = `{ id: int, number: str, type: str, supplierId: int, date: str, lines: T14[], files: T7[], externalNumber?: str|null, createdAt?: str, ledger?: T26 }`
|
||||||
- `T5` = `{ labeltemplate: T6 }`
|
- `T5` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T27|null, incoming?: { date: str, amount: num }[] }`
|
||||||
- `T6` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: T15, thumb?: str|null }`
|
- `T6` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: { objects: T15[] }, thumb?: str|null }`
|
||||||
- `T7` = `{ order: T10 }`
|
- `T7` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created?: str, modified?: str, skeledash?: str|null, thumbx?: int|null, thumby?: int|null, meta?: { [k]: str }|null }`
|
||||||
- `T8` = `{ quote: T10 }`
|
- `T8` = `{ id: int, entityId: int, addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }`
|
||||||
- `T9` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created?: str, modified?: str, thumbavif?: str|null, thumbx?: int|null, thumby?: int|null, meta?: { [k]: str }|null }`
|
- `T9` = `{ id: int, manifestarticle_id: int, transaction_type: str, amount: num, transaction_text: str, hash_version: int, prev_hash?: str, sku?: str|null, created_at?: str }`
|
||||||
- `T10` = `{ id: int, number: str, type: str, entityId: int|null, date: str, lines: T19[], createdAt?: str }`
|
- `T10` = `{ id: int, name: str, sort: int, exclusive: bool, values: T18[], articleCount?: int }`
|
||||||
- `T11` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T39|null }`
|
- `T11` = `{ id: int, sku: str|null, name: str|null, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str|null, shippable: bool }`
|
||||||
- `T12` = `{ id: int, entityId: int, addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }`
|
- `T12` = `{ amount: num, net: num, taxrate: num, sku?: str|null, name?: str|null, pu?: num, rebate?: num, comment?: str|null, shippable?: bool }`
|
||||||
- `T13` = `{ ledger: T14[] }`
|
- `T13` = `{ id: int, manifestId: int, number: str, carrierId: int, type: str, state: str, trackingCode?: str|null, carrier?: str|null }`
|
||||||
- `T14` = `{ id: int, manifestarticle_id: int, transaction_type: str, amount: num, transaction_text: str, hash_version: int, prev_hash?: str, sku?: str|null, created_at?: str }`
|
- `T14` = `{ id: int, sku: str|null, name: str|null, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str|null }`
|
||||||
- `T15` = `{ objects: T20[] }`
|
- `T15` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }`
|
||||||
- `T16` = `{ objects: T21[] }`
|
- `T16` = `{ id: int, customergroupId: int, firstName: str|null, name: str, number: str|null, createdAt: str, updatedAt: str }`
|
||||||
- `T17` = `{ printer: T22 }`
|
- `T17` = `{ si: str, basesi: str, basesifactor: num, symbol?: str|null, name?: str|null }`
|
||||||
- `T18` = `{ entity: T26 }`
|
- `T18` = `{ id: int, attributeId: int, value: str, sort: int, articleCount?: int }`
|
||||||
- `T19` = `{ id: int, sku: str|null, name: str|null, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str|null }`
|
- `T19` = `{ id: int, googleAuthId: int, email: str, role: str, picture?: str|null }`
|
||||||
- `T20` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }`
|
- `T20` = `{ id: int, name: str, sort: int, exclusive: bool, articleCount?: int }`
|
||||||
- `T21` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }`
|
- `T21` = `{ id: int, accountId: int, manifestId: int, type: str, amount: num }`
|
||||||
- `T22` = `{ id: int, company_id: int, address: str, type: str, sort: int, model?: str|null, serial?: str|null, desc?: str|null }`
|
- `T22` = `{ id: int, name: str, sort: int, hasPackages: bool, isReverse: bool }`
|
||||||
- `T23` = `{ id: int, name: str, sort: int, exclusive: bool, values: T34[] }`
|
- `T23` = `{ fullHash: str, thumbW: int, thumbH: int, thumbHash: str }`
|
||||||
- `T24` = `{ amount: num, net: num, taxrate: num, sku?: str|null, name?: str|null, pu?: num, rebate?: num, comment?: str|null }`
|
- `T24` = `{ id: int, entityId: int, contactType: str, contact: str }`
|
||||||
- `T25` = `{ sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }`
|
- `T25` = `{ id: int, name: str, createdAt: str, updatedAt: str }`
|
||||||
- `T26` = `{ id: int, customergroupId: int, firstName: str|null, name: str, number: str|null, createdAt: str, updatedAt: str }`
|
- `T26` = `{ totalPositions: int, completelyOnLedgerPositions: int }`
|
||||||
- `T27` = `{ name: str, id?: int|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null }`
|
- `T27` = `{ net: num, taxId: int, taxRate?: num|null }`
|
||||||
- `T28` = `{ id: int, sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }`
|
|
||||||
- `T29` = `{ article: T31 }`
|
|
||||||
- `T30` = `{ si: str, basesi: str, basesifactor: num, symbol?: str|null, name?: str|null }`
|
|
||||||
- `T31` = `{ id: int, categoryId: int, name: str, sku: str, track_stock: bool }`
|
|
||||||
- `T32` = `{ minmultiplier: num, net: num, taxRate: num, customergroupId: int }`
|
|
||||||
- `T33` = `{ id: int, entityId: int, contactType: str, contact: str }`
|
|
||||||
- `T34` = `{ id: int, attributeId: int, value: str, sort: int }`
|
|
||||||
- `T35` = `{ id: int, name: str, createdAt: str, updatedAt: str }`
|
|
||||||
- `T36` = `{ totalPositions: int, completelyOnLedgerPositions: int }`
|
|
||||||
- `T37` = `{ id: int, name: str, sort: int, exclusive: bool }`
|
|
||||||
- `T38` = `{ warehouse: T40 }`
|
|
||||||
- `T39` = `{ net: num, taxId: int, taxRate?: num|null }`
|
|
||||||
- `T40` = `{ id: int, name: str, sort: int }`
|
|
||||||
|
|
||||||
### Routes
|
### Routes
|
||||||
|
|
||||||
|
|
||||||
|
### Section: /article
|
||||||
|
|
||||||
#### Create article in category — `POST /article`
|
#### Create article in category — `POST /article`
|
||||||
- Params: `body: { categoryId: int, name: str, desc?: str, seo?: str, track_stock?: bool }`
|
- Params: `body: { categoryId: int, name: str, desc?: str, seo?: str, track_stock?: bool }`
|
||||||
- Returns: `201 T29`
|
- Returns: `201 { sku: str }`
|
||||||
|
#### Query company-wide article history — `GET /article/history`
|
||||||
|
- Params: `query: offset?: int, limit?: int, search?: str, seekMonths?: int`
|
||||||
|
- Returns: `200 { history: { id: int, date: str, sku: str, historytext: str, name?: str|null }[], total: int, unfilteredTotal: int, offset: int, limit: int }`
|
||||||
#### Delete article by SKU — `DELETE /article/{sku}`
|
#### Delete article by SKU — `DELETE /article/{sku}`
|
||||||
- Params: `sku: str, query: cascade?: bool`
|
- Params: `sku: str, query: cascade?: bool`
|
||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
#### Get article by SKU — `GET /article/{sku}`
|
#### Get article by SKU — `GET /article/{sku}`
|
||||||
- Params: `sku: str`
|
- Params: `sku: str`
|
||||||
- Returns: `200 T1`
|
- Returns: `200 { article: { id: int, sku: str, name: str, slug: str, desc: str|null, seo: str|null, track_stock: bool, unitamount: num|null, weight: num|null, unit: T17, brand: { name: str, id?: int|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null }, categoryIds: int[], photos: { sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }[], eans: str[], prices: { minmultiplier: num, net: num, taxRate: num, customergroupId: int }[], suppliers: T5[], undeliveredOrders: { orderId: int, orderNumber: str, date: str, amount: num, customerName?: str|null }[] } }`
|
||||||
#### Update article by SKU — `PATCH /article/{sku}`
|
#### Update article by SKU — `PATCH /article/{sku}`
|
||||||
- Params: `sku: str, body: { name?: str, desc?: str, seo?: str, unitamount?: num, unitSi?: str, track_stock?: bool }`
|
- Params: `sku: str, body: { name?: str, desc?: str, seo?: str, unitamount?: num, unitSi?: str, track_stock?: bool }`
|
||||||
- Returns: `200 T1`
|
- Returns: `204 void`
|
||||||
#### Get Stückliste (bundle items) for an article — `GET /article/{sku}/articlelist`
|
#### Get Stückliste (bundle items) for an article — `GET /article/{sku}/articlelist`
|
||||||
- Params: `sku: str`
|
- Params: `sku: str`
|
||||||
- Returns: `200 { items: { id: int, articleId: int, sku: str, name: str, count: int, sort: int }[] }`
|
- Returns: `200 { items: { id: int, articleId: int, sku: str, name: str, count: int, sort: int }[] }`
|
||||||
@@ -78,28 +71,28 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body).
|
|||||||
- Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, articleId?: int, exclusive?: bool }[] }`
|
- Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, articleId?: int, exclusive?: bool }[] }`
|
||||||
#### Set or clear article brand by SKU (null to clear) — `PUT /article/{sku}/brand`
|
#### Set or clear article brand by SKU (null to clear) — `PUT /article/{sku}/brand`
|
||||||
- Params: `sku: str, body: { brandId: int|null }`
|
- Params: `sku: str, body: { brandId: int|null }`
|
||||||
- Returns: `200 T1`
|
- Returns: `204 void`
|
||||||
#### Replace article categories by SKU — `PUT /article/{sku}/categories`
|
#### Replace article categories by SKU — `PUT /article/{sku}/categories`
|
||||||
- Params: `sku: str, body: { categoryIds: int[] }`
|
- Params: `sku: str, body: { categoryIds: int[] }`
|
||||||
- Returns: `200 T1`
|
- Returns: `204 void`
|
||||||
#### List child articles of a parent article — `GET /article/{sku}/children`
|
#### List child articles of a parent article — `GET /article/{sku}/children`
|
||||||
- Params: `sku: str`
|
- Params: `sku: str`
|
||||||
- Returns: `200 { children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }`
|
- Returns: `200 { children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }`
|
||||||
#### Create and attach a new child article to a parent article — `POST /article/{sku}/children`
|
#### Create and attach a new child article to a parent article — `POST /article/{sku}/children`
|
||||||
- Params: `sku: str, body: { attributeValueId?: int, value?: str, name?: str }`
|
- Params: `sku: str, body: { attributeValueId?: int, value?: str, name?: str }`
|
||||||
- Returns: `201 T29`
|
- Returns: `201 { sku: str }`
|
||||||
#### Attach an existing article as a child to a parent article — `PUT /article/{sku}/children`
|
#### Attach an existing article as a child to a parent article — `PUT /article/{sku}/children`
|
||||||
- Params: `sku: str, body: { sku: str, attributeValueId?: int, value?: str, reparent?: bool }`
|
- Params: `sku: str, body: { sku: str, attributeValueId?: int, value?: str, reparent?: bool }`
|
||||||
- Returns: `200 { article: T31, children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }`
|
- Returns: `200 { children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }`
|
||||||
#### Remove a child article from a parent article — `DELETE /article/{sku}/children/{childSku}`
|
#### Remove a child article from a parent article — `DELETE /article/{sku}/children/{childSku}`
|
||||||
- Params: `sku: str, childSku: str, query: deleteArticle?: bool`
|
- Params: `sku: str, childSku: str, query: deleteArticle?: bool`
|
||||||
- Returns: `200 { success: bool, deleted: bool }`
|
- Returns: `200 { success: bool, deleted: bool }`
|
||||||
#### Replace article EANs by SKU — `PUT /article/{sku}/eans`
|
#### Replace article EANs by SKU — `PUT /article/{sku}/eans`
|
||||||
- Params: `sku: str, body: { eans: str[] }`
|
- Params: `sku: str, body: { eans: str[] }`
|
||||||
- Returns: `200 T1`
|
- Returns: `204 void`
|
||||||
#### Get article history by SKU — `GET /article/{sku}/history`
|
#### Get article history by SKU — `GET /article/{sku}/history`
|
||||||
- Params: `sku: str, query: limit?: int`
|
- Params: `sku: str, query: offset?: int, limit?: int, search?: str, seekMonths?: int`
|
||||||
- Returns: `200 { history: { id: int, date: str, text: str }[], truncated: bool }`
|
- Returns: `200 { history: { id: int, date: str, text: str }[], total: int, unfilteredTotal: int, offset: int, limit: int }`
|
||||||
#### Delete article image — `DELETE /article/{sku}/image/{fullHash}`
|
#### Delete article image — `DELETE /article/{sku}/image/{fullHash}`
|
||||||
- Params: `sku: str, fullHash: str`
|
- Params: `sku: str, fullHash: str`
|
||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
@@ -114,12 +107,12 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body).
|
|||||||
- Returns: `200 { options: { id: int, articleId: int, sku: str, name: str, count: int, sort: int, mandated: bool }[] }`
|
- Returns: `200 { options: { id: int, articleId: int, sku: str, name: str, count: int, sort: int, mandated: bool }[] }`
|
||||||
#### Toggle parent mode for an article — `POST /article/{sku}/parent`
|
#### Toggle parent mode for an article — `POST /article/{sku}/parent`
|
||||||
- Params: `sku: str, body: { isParent: bool, attributeNameId?: int }`
|
- Params: `sku: str, body: { isParent: bool, attributeNameId?: int }`
|
||||||
- Returns: `200 T29`
|
- Returns: `204 void`
|
||||||
#### Replace POS prices by SKU (net > gross; price=net, tax=taxRate %) — `PUT /article/{sku}/prices`
|
#### Replace POS prices by SKU (net > gross) — `PUT /article/{sku}/prices`
|
||||||
- Params: `sku: str, body: { prices: { net?: num, gross?: num, price?: num, taxRate?: num, tax?: num, minmultiplier?: num }[] }`
|
- Params: `sku: str, body: { prices: { net?: num, gross?: num, taxRate?: num, minmultiplier?: num }[] }`
|
||||||
- Returns: `200 T1`
|
- Returns: `204 void`
|
||||||
#### Print article label — `POST /article/{sku}/print`
|
#### Print article label — `POST /article/{sku}/print`
|
||||||
- Params: `sku: str, body: { templateId: int, printerId: int, quantity: int }`
|
- Params: `sku: str, body: { templateId: int, agentprinterId: int, quantity: int }`
|
||||||
- Returns: `200 { success: bool }`
|
- Returns: `200 { success: bool }`
|
||||||
#### List article properties — `GET /article/{sku}/property`
|
#### List article properties — `GET /article/{sku}/property`
|
||||||
- Params: `sku: str`
|
- Params: `sku: str`
|
||||||
@@ -132,10 +125,10 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body).
|
|||||||
- Returns: `200 { ok: bool }`
|
- Returns: `200 { ok: bool }`
|
||||||
#### List article suppliers — `GET /article/{sku}/supplier`
|
#### List article suppliers — `GET /article/{sku}/supplier`
|
||||||
- Params: `sku: str`
|
- Params: `sku: str`
|
||||||
- Returns: `200 { suppliers: T11[] }`
|
- Returns: `200 { suppliers: T5[] }`
|
||||||
#### Assign supplier to article — `POST /article/{sku}/supplier`
|
#### Assign supplier to article — `POST /article/{sku}/supplier`
|
||||||
- Params: `sku: str, body: { entityId: int, sku?: str|null, pu?: num, stock?: num|null, sort?: int|null, netPrice?: num|null, taxId?: int|null, taxRate?: num|null }`
|
- Params: `sku: str, body: { entityId: int, sku?: str|null, pu?: num, stock?: num|null, sort?: int|null, netPrice?: num|null, taxId?: int|null, taxRate?: num|null }`
|
||||||
- Returns: `201 { supplier: T11 }`
|
- Returns: `201 { supplier: T5 }`
|
||||||
#### Reorder article suppliers — `PUT /article/{sku}/supplier/order`
|
#### Reorder article suppliers — `PUT /article/{sku}/supplier/order`
|
||||||
- Params: `sku: str, body: { ids: int[] }`
|
- Params: `sku: str, body: { ids: int[] }`
|
||||||
- Returns: `200 { ids: int[] }`
|
- Returns: `200 { ids: int[] }`
|
||||||
@@ -144,28 +137,31 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body).
|
|||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
#### Update article supplier assignment — `PATCH /article/{sku}/supplier/{id}`
|
#### Update article supplier assignment — `PATCH /article/{sku}/supplier/{id}`
|
||||||
- Params: `sku: str, id: int, body: { sku?: str|null, pu?: num, stock?: num|null, sort?: int, netPrice?: num|null, taxId?: int|null, taxRate?: num|null }`
|
- Params: `sku: str, id: int, body: { sku?: str|null, pu?: num, stock?: num|null, sort?: int, netPrice?: num|null, taxId?: int|null, taxRate?: num|null }`
|
||||||
- Returns: `200 { supplier: T11 }`
|
- Returns: `200 { supplier: T5 }`
|
||||||
#### Set article track_stock flag by SKU — `PUT /article/{sku}/track_stock`
|
#### Set article track_stock flag by SKU — `PUT /article/{sku}/track_stock`
|
||||||
- Params: `sku: str, body: { track_stock: bool }`
|
- Params: `sku: str, body: { track_stock: bool }`
|
||||||
- Returns: `200 T1`
|
- Returns: `204 void`
|
||||||
#### Set article weight by SKU — `PUT /article/{sku}/weight`
|
#### Set article weight by SKU — `PUT /article/{sku}/weight`
|
||||||
- Params: `sku: str, body: { weight: num }`
|
- Params: `sku: str, body: { weight: num }`
|
||||||
- Returns: `200 T1`
|
- Returns: `204 void`
|
||||||
|
|
||||||
|
### Section: /attributename
|
||||||
|
|
||||||
#### List attribute names — `GET /attributename`
|
#### List attribute names — `GET /attributename`
|
||||||
_none_
|
_none_
|
||||||
- Returns: `200 { attributenames: T23[] }`
|
- Returns: `200 { attributenames: T10[] }`
|
||||||
#### Create attribute name — `POST /attributename`
|
#### Create attribute name — `POST /attributename`
|
||||||
- Params: `body: { name: str, sort?: int, exclusive?: bool }`
|
- Params: `body: { name: str, sort?: int, exclusive?: bool }`
|
||||||
- Returns: `201 { attributename: T23 }`
|
- Returns: `201 { attributename: T10 }`
|
||||||
#### Reorder attribute names — `PUT /attributename/order`
|
#### Reorder attribute names — `PUT /attributename/order`
|
||||||
- Params: `body: { attributenameIds: int[] }`
|
- Params: `body: { attributenameIds: int[] }`
|
||||||
- Returns: `200 { ok: bool }`
|
- Returns: `200 { ok: bool }`
|
||||||
#### List values for an attribute — `GET /attributename/{attributeId}/value`
|
#### List values for an attribute — `GET /attributename/{attributeId}/value`
|
||||||
- Params: `attributeId: int`
|
- Params: `attributeId: int`
|
||||||
- Returns: `200 { values: T34[] }`
|
- Returns: `200 { values: T18[] }`
|
||||||
#### Create value for an attribute — `POST /attributename/{attributeId}/value`
|
#### Create value for an attribute — `POST /attributename/{attributeId}/value`
|
||||||
- Params: `attributeId: int, body: { value: str, sort?: int }`
|
- Params: `attributeId: int, body: { value: str, sort?: int }`
|
||||||
- Returns: `201 { attributevalue: T34 }`
|
- Returns: `201 { attributevalue: T18 }`
|
||||||
#### Reorder values of an attribute — `PUT /attributename/{attributeId}/value/order`
|
#### Reorder values of an attribute — `PUT /attributename/{attributeId}/value/order`
|
||||||
- Params: `attributeId: int, body: { attributevalueIds: int[] }`
|
- Params: `attributeId: int, body: { attributevalueIds: int[] }`
|
||||||
- Returns: `200 { ok: bool }`
|
- Returns: `200 { ok: bool }`
|
||||||
@@ -174,34 +170,73 @@ _none_
|
|||||||
- Returns: `200 { ok: bool }`
|
- Returns: `200 { ok: bool }`
|
||||||
#### Update attribute name — `PATCH /attributename/{id}`
|
#### Update attribute name — `PATCH /attributename/{id}`
|
||||||
- Params: `id: int, body: { name?: str, sort?: int, exclusive?: bool }`
|
- Params: `id: int, body: { name?: str, sort?: int, exclusive?: bool }`
|
||||||
- Returns: `200 { attributename: T23 }`
|
- Returns: `200 { attributename: T10 }`
|
||||||
|
|
||||||
|
### Section: /attributevalue
|
||||||
|
|
||||||
#### Delete attribute value — `DELETE /attributevalue/{id}`
|
#### Delete attribute value — `DELETE /attributevalue/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `200 { ok: bool }`
|
- Returns: `200 { ok: bool }`
|
||||||
#### Update attribute value — `PATCH /attributevalue/{id}`
|
#### Update attribute value — `PATCH /attributevalue/{id}`
|
||||||
- Params: `id: int, body: { value?: str, sort?: int }`
|
- Params: `id: int, body: { value?: str, sort?: int }`
|
||||||
- Returns: `200 { attributevalue: T34 }`
|
- Returns: `200 { attributevalue: T18 }`
|
||||||
#### Login with Google ID token — `POST /auth/login`
|
#### Delete attribute value image — `DELETE /attributevalue/{id}/image`
|
||||||
- Params: `body: { idToken: str }`
|
- Params: `id: int`
|
||||||
- Returns: `200 { message: str, user: { id: int, email: str, selectedCompanyId: int|null, picture?: str } }`
|
- Returns: `200 { attributevalue: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T23[] } }`
|
||||||
#### Logout — `POST /auth/logout`
|
|
||||||
|
### Section: /autoinvite
|
||||||
|
|
||||||
|
#### List auto-invitations — `GET /autoinvite`
|
||||||
_none_
|
_none_
|
||||||
- Returns: `200 { message: str }`
|
- Returns: `200 { autoinvites: { id: int, email: str, role: str, createdAt: str }[] }`
|
||||||
|
#### Create auto-invitation — `POST /autoinvite`
|
||||||
|
- Params: `body: { email: str, role?: str }`
|
||||||
|
- Returns: `201 { autoinvite: { id: int, email: str, role: str } }`
|
||||||
|
#### Delete auto-invitation — `DELETE /autoinvite/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `204 void`
|
||||||
|
|
||||||
|
### Section: /brand
|
||||||
|
|
||||||
#### List brands — `GET /brand`
|
#### List brands — `GET /brand`
|
||||||
_none_
|
- Params: `query: offset?: int, limit?: int, includeSmallThumbs?: bool`
|
||||||
- Returns: `200 { brands: { id: int, name: str, pics: T28[], gsprpinfo?: str|null }[] }`
|
- Returns: `200 { brands: { id: int, name: str, articleCount: int, pics: { fullHash: str, thumbW: int, thumbH: int, thumbHash: str, smallThumbW?: int|null, smallThumbH?: int|null, smallThumbBinary?: str|null }[], gsprpinfo?: str|null }[], total: int, offset: int, limit: int }`
|
||||||
#### Upsert brand — `PUT /brand`
|
#### Create brand — `POST /brand`
|
||||||
- Params: `body: { name: str, gsprpinfo?: str }`
|
- Params: `body: { name: str, gsprpinfo?: str }`
|
||||||
- Returns: `200 { brand: { id: int, name: str, pics: T28[], gsprpinfo?: str|null } }`
|
- Returns: `201 { brand: { id: int, name: str, pics: T23[], gsprpinfo?: str|null } }`
|
||||||
#### Delete brand — `DELETE /brand/{id}`
|
#### Delete brand — `DELETE /brand/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
|
#### Rename brand — `PATCH /brand/{id}`
|
||||||
|
- Params: `id: int, body: { name?: str, gsprpinfo?: str }`
|
||||||
|
- Returns: `200 { brand: { id: int, name: str, touchedArticleCount: int, gsprpinfo?: str|null } }`
|
||||||
#### Delete brand image — `DELETE /brand/{id}/image`
|
#### Delete brand image — `DELETE /brand/{id}/image`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `200 { brand: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T28[] } }`
|
- Returns: `200 { brand: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T23[] } }`
|
||||||
#### Add or replace brand image — `POST /brand/{id}/image`
|
|
||||||
- Params: `id: int, image/avif`
|
### Section: /carrier
|
||||||
- Returns: `201 { brand: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T28[] } }`
|
|
||||||
|
#### List carriers — `GET /carrier`
|
||||||
|
_none_
|
||||||
|
- Returns: `200 { carriers: T22[] }`
|
||||||
|
#### Create carrier — `POST /carrier`
|
||||||
|
- Params: `body: { name: str, sort?: int, hasPackages?: bool, isReverse?: bool }`
|
||||||
|
- Returns: `201 { carrier: T22 }`
|
||||||
|
#### Reorder carriers — `PUT /carrier/order`
|
||||||
|
- Params: `body: { carrierIds: int[] }`
|
||||||
|
- Returns: `200 { carrierIds: int[] }`
|
||||||
|
#### Delete carrier — `DELETE /carrier/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `204 void`
|
||||||
|
#### Get carrier by ID — `GET /carrier/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200 { carrier: T22 }`
|
||||||
|
#### Update carrier — `PATCH /carrier/{id}`
|
||||||
|
- Params: `id: int, body: { name?: str, sort?: int, hasPackages?: bool, isReverse?: bool }`
|
||||||
|
- Returns: `200 { carrier: T22 }`
|
||||||
|
|
||||||
|
### Section: /category
|
||||||
|
|
||||||
#### Create category — `POST /category`
|
#### Create category — `POST /category`
|
||||||
- Params: `body: { parentId: int, name: str }`
|
- Params: `body: { parentId: int, name: str }`
|
||||||
- Returns: `201 { category: { id: int, parentId: int|null, sort: int, name: str } }`
|
- Returns: `201 { category: { id: int, parentId: int|null, sort: int, name: str } }`
|
||||||
@@ -210,16 +245,16 @@ _none_
|
|||||||
- Returns: `200 { categoryIds: int[] }`
|
- Returns: `200 { categoryIds: int[] }`
|
||||||
#### Category tree — `GET /category/tree`
|
#### Category tree — `GET /category/tree`
|
||||||
_none_
|
_none_
|
||||||
- Returns: `200 { languageId: int, tree: { id: int, sort: int, posterminalIds: int[], parentId?: int|null, name?: str|null, slug?: str|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null }[], articles: { id: int, sku: str, name: str, slug: str, track_stock: bool, optionsCount: int, articleListCount: int, childCount: int, net?: num|null, taxRate?: num|null, parentArticleId?: int|null, parentSku?: str|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null, brand?: T27|null }[] }`
|
- Returns: `200 { languageId: int, tree: { id: int, sort: int, posterminalIds: int[], parentId?: int|null, name?: str|null, slug?: str|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null, articleCount?: int }[], articleCount: int }`
|
||||||
#### Delete category — `DELETE /category/{id}`
|
#### Delete category — `DELETE /category/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int, query: recursive?: bool`
|
||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
#### Update category (rename or move) — `PATCH /category/{id}`
|
#### Update category (rename or move) — `PATCH /category/{id}`
|
||||||
- Params: `id: int, body: { name?: str, parentId?: int }`
|
- Params: `id: int, body: { name?: str, parentId?: int }`
|
||||||
- Returns: `200 { category: { id: int, name: str, slug: str, parentId: int, sort: int } }`
|
- Returns: `200 { category: { id: int, name: str, slug: str, parentId: int, sort: int } }`
|
||||||
#### Articles in category — `GET /category/{id}/articles`
|
#### Articles in category — `GET /category/{id}/articles`
|
||||||
- Params: `id: int`
|
- Params: `id: int, query: includeChildren?: bool`
|
||||||
- Returns: `200 { articles: { id: int, sku: str, name: str, slug: str, track_stock: bool, optionsCount: int, articleListCount: int, childCount: int, net?: num|null, taxRate?: num|null, parentArticleId?: int|null, parentSku?: str|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null, brand?: T27|null }[] }`
|
- Returns: `200 { articles: { id: int, sku: str, name: str, slug: str, track_stock: bool, optionsCount: int, articleListCount: int, childCount: int, net?: num|null, taxRate?: num|null, parentArticleId?: int|null, parentSku?: str|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null, smallThumbW?: int|null, smallThumbH?: int|null, smallThumbBinary?: str|null, brand?: { name: str, id?: int|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null }, articlewarehousesummary?: { id: int, articleId: int, warehouseId: int, stock: num, article_id?: int, warehouse_id?: int, warehouseName?: str|null }[], warehouses?: { id: int, name: str, stock: num }[] }[] }`
|
||||||
#### List category attribute values — `GET /category/{id}/attributevalue`
|
#### List category attribute values — `GET /category/{id}/attributevalue`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, categoryId?: int, exclusive?: bool }[] }`
|
- Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, categoryId?: int, exclusive?: bool }[] }`
|
||||||
@@ -229,6 +264,9 @@ _none_
|
|||||||
#### Delete category image — `DELETE /category/{id}/image`
|
#### Delete category image — `DELETE /category/{id}/image`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `200 { category: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null } }`
|
- Returns: `200 { category: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null } }`
|
||||||
|
#### Generate category mosaic image from contents — `POST /category/{id}/image/generate`
|
||||||
|
- Params: `id: int, body?: { size?: int }`
|
||||||
|
- Returns: `200 { category: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, size: int, grid: str, articleCount: int } }`
|
||||||
#### List category properties — `GET /category/{id}/property`
|
#### List category properties — `GET /category/{id}/property`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `200 { properties: { id: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int, categoryId?: int }[] }`
|
- Returns: `200 { properties: { id: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int, categoryId?: int }[] }`
|
||||||
@@ -238,9 +276,12 @@ _none_
|
|||||||
#### Delete category property — `DELETE /category/{id}/property/{propertyId}`
|
#### Delete category property — `DELETE /category/{id}/property/{propertyId}`
|
||||||
- Params: `id: int, propertyId: int`
|
- Params: `id: int, propertyId: int`
|
||||||
- Returns: `200 { ok: bool }`
|
- Returns: `200 { ok: bool }`
|
||||||
|
|
||||||
|
### Section: /company
|
||||||
|
|
||||||
#### List companies — `GET /company`
|
#### List companies — `GET /company`
|
||||||
_none_
|
_none_
|
||||||
- Returns: `200 { companies: { id: int, iso639: str, name: str, taxrate: num, reducedtaxrate: num }[] }`
|
- Returns: `200 { companies: { id: int, name: str, rootCategoryId: int|null, languageId: int|null, iso639: str|null, taxrate: num, reducedtaxrate: num, taxfreerate: num, role: str }[] }`
|
||||||
#### Get company history — `GET /company/history`
|
#### Get company history — `GET /company/history`
|
||||||
- Params: `query: limit?: int`
|
- Params: `query: limit?: int`
|
||||||
- Returns: `200 { history: { id: int, date: str, text: str, companyuserId?: int|null }[], truncated: bool }`
|
- Returns: `200 { history: { id: int, date: str, text: str, companyuserId?: int|null }[], truncated: bool }`
|
||||||
@@ -261,13 +302,31 @@ _none_
|
|||||||
- Returns: `200 { setting: { key: str, value: str|null } }`
|
- Returns: `200 { setting: { key: str, value: str|null } }`
|
||||||
#### Update company — `PUT /company/{id}`
|
#### Update company — `PUT /company/{id}`
|
||||||
- Params: `id: int, body: { iso639: str, name: str, taxrate: num, reducedtaxrate: num }`
|
- Params: `id: int, body: { iso639: str, name: str, taxrate: num, reducedtaxrate: num }`
|
||||||
- Returns: `200 { company: { id: int, iso639: str, name: str, taxrate: num, reducedtaxrate: num } }`
|
- Returns: `200 { company: { id: int, name: str, rootCategoryId: int|null, languageId: int|null, iso639: str|null, taxrate: num, reducedtaxrate: num, taxfreerate: num, role: str } }`
|
||||||
|
|
||||||
|
### Section: /companyuser
|
||||||
|
|
||||||
|
#### List company users — `GET /companyuser`
|
||||||
|
_none_
|
||||||
|
- Returns: `200 { companyusers: T19[] }`
|
||||||
|
#### Add an existing user to the company — `POST /companyuser`
|
||||||
|
- Params: `body: { email: str, role?: str }`
|
||||||
|
- Returns: `201 { companyuser: T19 }`
|
||||||
|
#### Remove a user from the company — `DELETE /companyuser/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `204 void`
|
||||||
|
#### Change a company user's role — `PATCH /companyuser/{id}`
|
||||||
|
- Params: `id: int, body: { role: str }`
|
||||||
|
- Returns: `200 { companyuser: T19 }`
|
||||||
|
|
||||||
|
### Section: /customergroup
|
||||||
|
|
||||||
#### List customer groups — `GET /customergroup`
|
#### List customer groups — `GET /customergroup`
|
||||||
_none_
|
_none_
|
||||||
- Returns: `200 { customergroups: { id: int, name: str, sort: int, editable: bool }[] }`
|
- Returns: `200 { customergroups: { id: int, name: str, sort: int, editable: bool }[] }`
|
||||||
#### Create customer group — `POST /customergroup`
|
#### Create customer group — `POST /customergroup`
|
||||||
- Params: `body: { name: str }`
|
- Params: `body: { name: str }`
|
||||||
- Returns: `201 { customergroup: T40 }`
|
- Returns: `201 { customergroup: { id: int, name: str, sort: int } }`
|
||||||
#### Reorder customer groups — `PUT /customergroup/order`
|
#### Reorder customer groups — `PUT /customergroup/order`
|
||||||
- Params: `body: { customergroupIds: int[] }`
|
- Params: `body: { customergroupIds: int[] }`
|
||||||
- Returns: `200 { customergroupIds: int[] }`
|
- Returns: `200 { customergroupIds: int[] }`
|
||||||
@@ -276,145 +335,187 @@ _none_
|
|||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
#### Update customer group — `PATCH /customergroup/{id}`
|
#### Update customer group — `PATCH /customergroup/{id}`
|
||||||
- Params: `id: int, body: { name: str }`
|
- Params: `id: int, body: { name: str }`
|
||||||
- Returns: `200 { customergroup: T40 }`
|
- Returns: `200 { customergroup: { id: int, name: str, sort: int } }`
|
||||||
|
|
||||||
|
### Section: /delivery
|
||||||
|
|
||||||
#### List deliveries — `GET /delivery`
|
#### List deliveries — `GET /delivery`
|
||||||
_none_
|
- Params: `query: offset?: int, limit?: int, search?: str, onlyUnbooked?: bool, supplierId?: int, seekMonths?: int`
|
||||||
- Returns: `200 { deliveries: { id: int, number: str, type: str, supplierId: int|null, date: str, externalNumber?: str|null, createdAt?: str, ledger?: T36 }[] }`
|
- Returns: `200 { deliveries: { id: int, number: str, type: str, supplierId: int, date: str, externalNumber?: str|null, createdAt?: str, ledger?: T26 }[], total: int, unfilteredTotal: int, offset: int, limit: int }`
|
||||||
#### Create delivery — `POST /delivery`
|
#### Create delivery — `POST /delivery`
|
||||||
- Params: `body: { externalNumber: str, date: str, lines: T24[], supplierId?: int|null, files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }`
|
- Params: `body: { externalNumber: str, date: str, supplierId: int, lines: { amount: num, net: num, taxrate: num, sku?: str|null, name?: str|null, pu?: num, rebate?: num, comment?: str|null }[], files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }`
|
||||||
- Returns: `201 T3`
|
- Returns: `201 { delivery: T4 }`
|
||||||
#### Delete delivery — `DELETE /delivery/{id}`
|
#### Delete delivery — `DELETE /delivery/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
#### Get delivery — `GET /delivery/{id}`
|
#### Get delivery — `GET /delivery/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `200 T3`
|
- Returns: `200 { delivery: T4 }`
|
||||||
#### Update delivery — `PATCH /delivery/{id}`
|
#### Update delivery — `PATCH /delivery/{id}`
|
||||||
- Params: `id: int, body: { externalNumber?: str|null, date?: str, supplierId?: int|null, lines?: T24[], files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }`
|
- Params: `id: int, body: { externalNumber?: str|null, date?: str, supplierId?: int|null, lines?: { amount: num, net: num, taxrate: num, sku?: str|null, name?: str|null, pu?: num, rebate?: num, comment?: str|null }[], files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }`
|
||||||
- Returns: `200 T3`
|
- Returns: `200 { delivery: T4 }`
|
||||||
#### Get delivery attachment file content — `GET /delivery/{id}/file/{fileId}`
|
#### Delete delivery file attachment — `DELETE /delivery/{id}/file/{fileId}`
|
||||||
- Params: `id: int, fileId: int, query: download?: bool`
|
- Params: `id: int, fileId: int`
|
||||||
- Returns: `200 str`
|
- Returns: `204 void`
|
||||||
|
#### Generate and attach delivery note PDF (goods receipt) — `POST /delivery/{id}/pdf`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `201 { attachment: T7, fileUrl: str }`
|
||||||
|
|
||||||
|
### Section: /entity
|
||||||
|
|
||||||
#### List entities — `GET /entity`
|
#### List entities — `GET /entity`
|
||||||
- Params: `query: customergroupId?: int, role?: str`
|
- Params: `query: customergroupId?: int, role?: str`
|
||||||
- Returns: `200 { entities: T26[] }`
|
- Returns: `200 { entities: T16[] }`
|
||||||
#### Create entity — `POST /entity`
|
#### Create entity — `POST /entity`
|
||||||
- Params: `body: { customergroupId: int, name: str, firstName?: str|null, number?: str }`
|
- Params: `body: { customergroupId: int, name: str, firstName?: str|null, number?: str }`
|
||||||
- Returns: `201 T18`
|
- Returns: `201 { entity: T16 }`
|
||||||
#### Peek next customer number — `GET /entity/next-number`
|
#### Peek next customer number — `GET /entity/next-number`
|
||||||
_none_
|
_none_
|
||||||
- Returns: `200 { number: str }`
|
- Returns: `200 { number: str }`
|
||||||
#### List entity addresses — `GET /entity/{entityId}/address`
|
#### List entity addresses — `GET /entity/{entityId}/address`
|
||||||
- Params: `entityId: int`
|
- Params: `entityId: int`
|
||||||
- Returns: `200 { addresses: T12[] }`
|
- Returns: `200 { addresses: T8[] }`
|
||||||
#### Create entity address — `POST /entity/{entityId}/address`
|
#### Create entity address — `POST /entity/{entityId}/address`
|
||||||
- Params: `entityId: int, body: { addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }`
|
- Params: `entityId: int, body: { addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }`
|
||||||
- Returns: `201 { address: T12 }`
|
- Returns: `201 { address: T8 }`
|
||||||
#### Delete entity address — `DELETE /entity/{entityId}/address/{id}`
|
#### Delete entity address — `DELETE /entity/{entityId}/address/{id}`
|
||||||
- Params: `entityId: int, id: int`
|
- Params: `entityId: int, id: int`
|
||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
#### Update entity address — `PATCH /entity/{entityId}/address/{id}`
|
#### Update entity address — `PATCH /entity/{entityId}/address/{id}`
|
||||||
- Params: `entityId: int, id: int, body: { addressType?: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }`
|
- Params: `entityId: int, id: int, body: { addressType?: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }`
|
||||||
- Returns: `200 { address: T12 }`
|
- Returns: `200 { address: T8 }`
|
||||||
#### List entity contacts — `GET /entity/{entityId}/contact`
|
#### List entity contacts — `GET /entity/{entityId}/contact`
|
||||||
- Params: `entityId: int`
|
- Params: `entityId: int`
|
||||||
- Returns: `200 { contacts: T33[] }`
|
- Returns: `200 { contacts: T24[] }`
|
||||||
#### Create entity contact — `POST /entity/{entityId}/contact`
|
#### Create entity contact — `POST /entity/{entityId}/contact`
|
||||||
- Params: `entityId: int, body: { contactType: str, contact: str }`
|
- Params: `entityId: int, body: { contactType: str, contact: str }`
|
||||||
- Returns: `201 { contact: T33 }`
|
- Returns: `201 { contact: T24 }`
|
||||||
#### Delete entity contact — `DELETE /entity/{entityId}/contact/{id}`
|
#### Delete entity contact — `DELETE /entity/{entityId}/contact/{id}`
|
||||||
- Params: `entityId: int, id: int`
|
- Params: `entityId: int, id: int`
|
||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
#### Update entity contact — `PATCH /entity/{entityId}/contact/{id}`
|
#### Update entity contact — `PATCH /entity/{entityId}/contact/{id}`
|
||||||
- Params: `entityId: int, id: int, body: { contactType?: str, contact?: str }`
|
- Params: `entityId: int, id: int, body: { contactType?: str, contact?: str }`
|
||||||
- Returns: `200 { contact: T33 }`
|
- Returns: `200 { contact: T24 }`
|
||||||
#### Delete entity — `DELETE /entity/{id}`
|
#### Delete entity — `DELETE /entity/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
#### Get entity — `GET /entity/{id}`
|
#### Get entity — `GET /entity/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `200 T18`
|
- Returns: `200 { entity: T16 }`
|
||||||
#### Update entity — `PATCH /entity/{id}`
|
#### Update entity — `PATCH /entity/{id}`
|
||||||
- Params: `id: int, body: { name?: str, firstName?: str|null, number?: str|null }`
|
- Params: `id: int, body: { name?: str, firstName?: str|null, number?: str|null }`
|
||||||
- Returns: `200 T18`
|
- Returns: `200 { entity: T16 }`
|
||||||
|
|
||||||
|
### Section: /labeltemplate
|
||||||
|
|
||||||
#### List label templates — `GET /labeltemplate`
|
#### List label templates — `GET /labeltemplate`
|
||||||
_none_
|
_none_
|
||||||
- Returns: `200 { labeltemplates: { id: int, name: str, width: num, height: num, dpi: int, type: str, thumb?: str|null }[] }`
|
- Returns: `200 { labeltemplates: { id: int, name: str, width: num, height: num, dpi: int, type: str, thumb?: str|null }[] }`
|
||||||
#### Create label template — `POST /labeltemplate`
|
#### Create label template — `POST /labeltemplate`
|
||||||
- Params: `body: { name: str, width: num, height: num, dpi: int, type: str, content: T16 }`
|
- Params: `body: { name: str, width: num, height: num, dpi: int, type: str, content: { objects: T15[] } }`
|
||||||
- Returns: `201 T5`
|
- Returns: `201 { labeltemplate: T6 }`
|
||||||
|
#### Test print label template — `POST /labeltemplate/testprint`
|
||||||
|
- Params: `body: { agentprinterId: int, templateId?: int, objects?: any[], width?: int, height?: int, dpi?: int, sku?: str, quantity?: int }`
|
||||||
|
- Returns: `200 { success: bool }`
|
||||||
#### Delete label template — `DELETE /labeltemplate/{id}`
|
#### Delete label template — `DELETE /labeltemplate/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
#### Get label template by ID — `GET /labeltemplate/{id}`
|
#### Get label template by ID — `GET /labeltemplate/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `200 T5`
|
- Returns: `200 { labeltemplate: T6 }`
|
||||||
#### Update label template (partial) — `PATCH /labeltemplate/{id}`
|
#### Update label template (partial) — `PATCH /labeltemplate/{id}`
|
||||||
- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T16 }`
|
- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: { objects: T15[] } }`
|
||||||
- Returns: `200 T5`
|
- Returns: `200 { labeltemplate: T6 }`
|
||||||
#### Update label template — `PUT /labeltemplate/{id}`
|
#### Update label template — `PUT /labeltemplate/{id}`
|
||||||
- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T16 }`
|
- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: { objects: T15[] } }`
|
||||||
- Returns: `200 T5`
|
- Returns: `200 { labeltemplate: T6 }`
|
||||||
|
|
||||||
|
### Section: /ledger
|
||||||
|
|
||||||
#### Query ledger by manifest article ID or SKU — `GET /ledger`
|
#### Query ledger by manifest article ID or SKU — `GET /ledger`
|
||||||
- Params: `query: manifestarticle_id?: int, sku?: str, transaction_type?: str`
|
- Params: `query: manifestarticle_id?: int, sku?: str, transaction_type?: str`
|
||||||
- Returns: `200 T13`
|
- Returns: `200 { ledger: T9[] }`
|
||||||
#### Add ledger entry — `POST /ledger`
|
#### Query company-wide ledger history — `GET /ledger/history`
|
||||||
- Params: `body: { manifestarticle_id: int, transaction_type: str, amount: num, transaction_text: str }`
|
- Params: `query: offset?: int, limit?: int, search?: str, seekMonths?: int`
|
||||||
- Returns: `201 { ledger: T14 }`
|
- Returns: `200 { ledger: { id: int, date: str, transaction_type: str, transaction_text: str, sku?: str|null, name?: str|null, amount?: num }[], total: int, unfilteredTotal: int, offset: int, limit: int }`
|
||||||
#### Query ledger by manifest article ID — `GET /ledger/manifestarticle/{manifestarticleId}`
|
#### Query ledger by manifest article ID — `GET /ledger/manifestarticle/{manifestarticleId}`
|
||||||
- Params: `manifestarticleId: int, query: transaction_type?: str`
|
- Params: `manifestarticleId: int, query: transaction_type?: str`
|
||||||
- Returns: `200 T13`
|
- Returns: `200 { ledger: T9[] }`
|
||||||
#### Query ledger by article SKU — `GET /ledger/sku/{sku}`
|
#### Query ledger by article SKU — `GET /ledger/sku/{sku}`
|
||||||
- Params: `sku: str, query: transaction_type?: str`
|
- Params: `sku: str, query: transaction_type?: str`
|
||||||
- Returns: `200 T13`
|
- Returns: `200 { ledger: T9[] }`
|
||||||
|
|
||||||
|
### Section: /number
|
||||||
|
|
||||||
#### List number sequences — `GET /number`
|
#### List number sequences — `GET /number`
|
||||||
_none_
|
_none_
|
||||||
- Returns: `200 { numbers: { id: int, type: str, prefix: str, padding: int, next: int }[] }`
|
- Returns: `200 { numbers: { id: int, type: str, prefix: str, padding: int, next: int }[] }`
|
||||||
#### Update number sequence — `PATCH /number/{id}`
|
#### Update number sequence — `PATCH /number/{id}`
|
||||||
- Params: `id: int, body: { prefix?: str, padding?: int, next?: int }`
|
- Params: `id: int, body: { prefix?: str, padding?: int, next?: int }`
|
||||||
- Returns: `200 { number: { id: int, type: str, prefix: str, padding: int, next: int } }`
|
- Returns: `200 { number: { id: int, type: str, prefix: str, padding: int, next: int } }`
|
||||||
|
|
||||||
|
### Section: /order
|
||||||
|
|
||||||
#### List orders — `GET /order`
|
#### List orders — `GET /order`
|
||||||
_none_
|
- Params: `query: offset?: int, limit?: int, search?: str, onlyUnpaid?: bool, onlyUndelivered?: bool, seekMonths?: int`
|
||||||
- Returns: `200 { orders: { id: int, number: str, type: str, entityId: int|null, date: str, createdAt?: str }[] }`
|
- Returns: `200 { orders: T1[], total: int, unfilteredTotal: int, offset: int, limit: int }`
|
||||||
#### Create order — `POST /order`
|
#### Create order — `POST /order`
|
||||||
- Params: `body: { number: str, date: str, lines: T24[], entityId?: int|null }`
|
- Params: `body: { number: str, date: str, entityId: int, lines: T12[], externalNumber?: str|null }`
|
||||||
- Returns: `201 T7`
|
- Returns: `201 { order: T1 }`
|
||||||
#### Delete order — `DELETE /order/{id}`
|
#### Delete order — `DELETE /order/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
#### Get order — `GET /order/{id}`
|
#### Get order — `GET /order/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `200 T7`
|
- Returns: `200 { order: T1 }`
|
||||||
#### Update order — `PATCH /order/{id}`
|
#### Update order — `PATCH /order/{id}`
|
||||||
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T24[] }`
|
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, externalNumber?: str|null, lines?: T12[] }`
|
||||||
- Returns: `200 T7`
|
- Returns: `200 { order: T1 }`
|
||||||
#### List printers — `GET /printer`
|
#### Delete order file attachment — `DELETE /order/{id}/file/{fileId}`
|
||||||
_none_
|
- Params: `id: int, fileId: int`
|
||||||
- Returns: `200 { printers: T22[] }`
|
- Returns: `204 void`
|
||||||
#### Create printer — `POST /printer`
|
#### List payments of an order — `GET /order/{id}/payment`
|
||||||
- Params: `body: { address: str, type: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }`
|
- Params: `id: int`
|
||||||
- Returns: `201 T17`
|
- Returns: `200 { payments: T21[] }`
|
||||||
#### Reorder printers — `PUT /printer/order`
|
#### Add payment to an order — `POST /order/{id}/payment`
|
||||||
- Params: `body: { printerIds: int[] }`
|
- Params: `id: int, body: { accountId: int, type: str, amount: num }`
|
||||||
- Returns: `200 { printerIds: int[] }`
|
- Returns: `201 { payment: T21 }`
|
||||||
#### Delete printer — `DELETE /printer/{id}`
|
#### Generate and attach order PDF — `POST /order/{id}/pdf`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `201 { attachment: T7, fileUrl: str }`
|
||||||
|
#### List shipments of an order — `GET /order/{id}/shipment`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200 { shipments: T2[] }`
|
||||||
|
#### Create shipment for an order — `POST /order/{id}/shipment`
|
||||||
|
- Params: `id: int, body: { lines: { sku?: str, orderLineId?: int, amount?: num, name?: str, pu?: num, net?: num, rebate?: num, taxrate?: num, comment?: str }[], date?: str, warehouseId?: int, externalNumber?: str }`
|
||||||
|
- Returns: `201 { shipment: T2 }`
|
||||||
|
#### List shippings of an order — `GET /order/{id}/shipping`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200 { shippings: T13[] }`
|
||||||
|
#### Create shipping for an order — `POST /order/{id}/shipping`
|
||||||
|
- Params: `id: int, body: { carrierId: int, type: str, state: str, trackingCode?: str, warehouseId?: int }`
|
||||||
|
- Returns: `201 { shipping: T13 }`
|
||||||
|
|
||||||
|
### Section: /package
|
||||||
|
|
||||||
|
#### Delete package — `DELETE /package/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
#### Get printer by ID — `GET /printer/{id}`
|
|
||||||
|
### Section: /payment
|
||||||
|
|
||||||
|
#### Delete payment — `DELETE /payment/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `200 T17`
|
- Returns: `204 void`
|
||||||
#### Update printer (partial) — `PATCH /printer/{id}`
|
|
||||||
- Params: `id: int, body: { address?: str, type?: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }`
|
### Section: /propertyname
|
||||||
- Returns: `200 T17`
|
|
||||||
#### Update printer — `PUT /printer/{id}`
|
|
||||||
- Params: `id: int, body: { address?: str, type?: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }`
|
|
||||||
- Returns: `200 T17`
|
|
||||||
#### List property names — `GET /propertyname`
|
#### List property names — `GET /propertyname`
|
||||||
_none_
|
_none_
|
||||||
- Returns: `200 { propertynames: T37[] }`
|
- Returns: `200 { propertynames: T20[] }`
|
||||||
#### Create property name — `POST /propertyname`
|
#### Create property name — `POST /propertyname`
|
||||||
- Params: `body: { name: str, sort?: int, exclusive?: bool }`
|
- Params: `body: { name: str, sort?: int, exclusive?: bool }`
|
||||||
- Returns: `201 { propertyname: T37 }`
|
- Returns: `201 { propertyname: T20 }`
|
||||||
#### Reorder property names — `PUT /propertyname/order`
|
#### Reorder property names — `PUT /propertyname/order`
|
||||||
- Params: `body: { propertynameIds: int[] }`
|
- Params: `body: { propertynameIds: int[] }`
|
||||||
- Returns: `200 { ok: bool }`
|
- Returns: `200 { ok: bool }`
|
||||||
@@ -423,58 +524,178 @@ _none_
|
|||||||
- Returns: `200 { ok: bool }`
|
- Returns: `200 { ok: bool }`
|
||||||
#### Update property name — `PATCH /propertyname/{id}`
|
#### Update property name — `PATCH /propertyname/{id}`
|
||||||
- Params: `id: int, body: { name?: str, sort?: int, exclusive?: bool }`
|
- Params: `id: int, body: { name?: str, sort?: int, exclusive?: bool }`
|
||||||
- Returns: `200 { propertyname: T37 }`
|
- Returns: `200 { propertyname: T20 }`
|
||||||
|
|
||||||
|
### Section: /quote
|
||||||
|
|
||||||
#### List quotes — `GET /quote`
|
#### List quotes — `GET /quote`
|
||||||
_none_
|
- Params: `query: offset?: int, limit?: int, search?: str, seekMonths?: int`
|
||||||
- Returns: `200 { quotes: { id: int, number: str, type: str, entityId: int|null, date: str, createdAt?: str }[] }`
|
- Returns: `200 { quotes: T3[], total: int, unfilteredTotal: int, offset: int, limit: int }`
|
||||||
#### Create quote — `POST /quote`
|
#### Create quote — `POST /quote`
|
||||||
- Params: `body: { number: str, date: str, lines: T24[], entityId?: int|null }`
|
- Params: `body: { number: str, date: str, lines: T12[], entityId?: int|null, externalNumber?: str|null }`
|
||||||
- Returns: `201 T8`
|
- Returns: `201 { quote: T3 }`
|
||||||
#### Delete quote — `DELETE /quote/{id}`
|
#### Delete quote — `DELETE /quote/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
#### Get quote — `GET /quote/{id}`
|
#### Get quote — `GET /quote/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `200 T8`
|
- Returns: `200 { quote: T3 }`
|
||||||
#### Update quote — `PATCH /quote/{id}`
|
#### Update quote — `PATCH /quote/{id}`
|
||||||
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T24[] }`
|
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, externalNumber?: str|null, lines?: T12[] }`
|
||||||
- Returns: `200 T8`
|
- Returns: `200 { quote: T3 }`
|
||||||
|
#### Delete quote file attachment — `DELETE /quote/{id}/file/{fileId}`
|
||||||
|
- Params: `id: int, fileId: int`
|
||||||
|
- Returns: `204 void`
|
||||||
|
#### Create order from quote — `POST /quote/{id}/order`
|
||||||
|
- Params: `id: int, body?: { number?: str, date?: str, createdAt?: str, entityId?: int|null, externalNumber?: str|null, lines?: T12[] }`
|
||||||
|
- Returns: `201 { order: T1 }`
|
||||||
|
#### Generate and attach quote PDF — `POST /quote/{id}/pdf`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `201 { attachment: T7, fileUrl: str }`
|
||||||
|
|
||||||
|
### Section: /retoure
|
||||||
|
|
||||||
|
#### List retoures — `GET /retoure`
|
||||||
|
- Params: `query: offset?: int, limit?: int, search?: str, onlyUnpaid?: bool, onlyUndelivered?: bool, entityId?: int, seekMonths?: int`
|
||||||
|
- Returns: `200`
|
||||||
|
#### Create retoure — `POST /retoure`
|
||||||
|
- Params: `body: { date: str, number?: str, entityId?: int, externalNumber?: str, lines?: any[] }`
|
||||||
|
- Returns: `201`
|
||||||
|
#### Rebuild retoure summaries — `POST /retoure/rebuild-summaries`
|
||||||
|
_none_
|
||||||
|
- Returns: `200`
|
||||||
|
#### Delete retoure — `DELETE /retoure/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `204 void`
|
||||||
|
#### Get retoure by ID — `GET /retoure/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200`
|
||||||
|
#### Update retoure — `PUT /retoure/{id}`
|
||||||
|
- Params: `id: int, body: {}`
|
||||||
|
- Returns: `200`
|
||||||
|
#### Create shipment for retoure — `POST /retoure/{id}/shipment`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `201`
|
||||||
|
|
||||||
|
### Section: /return
|
||||||
|
|
||||||
|
#### List returns — `GET /return`
|
||||||
|
- Params: `query: offset?: int, limit?: int, search?: str, onlyUnbooked?: bool, supplierId?: int`
|
||||||
|
- Returns: `200`
|
||||||
|
#### Create return — `POST /return`
|
||||||
|
- Params: `body: {}`
|
||||||
|
- Returns: `201`
|
||||||
|
#### Rebuild return summaries — `POST /return/rebuild-summaries`
|
||||||
|
_none_
|
||||||
|
- Returns: `200`
|
||||||
|
#### Delete return — `DELETE /return/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `204 void`
|
||||||
|
#### Get return by ID — `GET /return/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200`
|
||||||
|
#### Update return — `PUT /return/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200`
|
||||||
|
#### Book return to ledger — `POST /return/{id}/book`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200`
|
||||||
|
|
||||||
|
### Section: /shipment
|
||||||
|
|
||||||
|
#### List shipments — `GET /shipment`
|
||||||
|
- Params: `query: offset?: int, limit?: int, search?: str, state?: str, seekMonths?: int`
|
||||||
|
- Returns: `200 { shipments: { id: int, number: str, type: str, date: str, positions: int, totalQuantity: num, packageCount: int, state: str, entityId?: int|null, externalNumber?: str|null, createdAt?: str|null, orderId?: int|null, orderNumber?: str|null, customerName?: str|null, carrier?: str|null, trackingCode?: str|null }[], total: int, unfilteredTotal: int, offset: int, limit: int }`
|
||||||
|
#### Delete shipment — `DELETE /shipment/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `204 void`
|
||||||
|
#### Get shipment by ID — `GET /shipment/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200 { shipment: T2 }`
|
||||||
|
#### Update shipment state or metadata — `PATCH /shipment/{id}`
|
||||||
|
- Params: `id: int, body: { state?: str }`
|
||||||
|
- Returns: `200 { shipment: T2 }`
|
||||||
|
#### Delete shipment file attachment — `DELETE /shipment/{id}/file/{fileId}`
|
||||||
|
- Params: `id: int, fileId: int`
|
||||||
|
- Returns: `204 void`
|
||||||
|
#### Generate and attach shipment PDF (delivery note) — `POST /shipment/{id}/pdf`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `201 { attachment: T7, fileUrl: str }`
|
||||||
|
#### List packages of a shipment — `GET /shipment/{id}/shipping`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200 { shippings: T13[] }`
|
||||||
|
#### Create package for a shipment — `POST /shipment/{id}/shipping`
|
||||||
|
- Params: `id: int, body: { carrierId: int, type: str, state: str, trackingCode?: str, warehouseId?: int }`
|
||||||
|
- Returns: `201 { shipping: T13 }`
|
||||||
|
|
||||||
|
### Section: /shipping
|
||||||
|
|
||||||
|
#### Delete shipping — `DELETE /shipping/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `204 void`
|
||||||
|
#### Update shipping — `PATCH /shipping/{id}`
|
||||||
|
- Params: `id: int, body: { type?: str, state?: str, trackingCode?: str, carrierId?: int, warehouseId?: int }`
|
||||||
|
- Returns: `200 { shipping: T13 }`
|
||||||
|
#### List packages of a shipping — `GET /shipping/{id}/package`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200 { packages: { id: int, shippingId: int, entityaddressId: int, number: str, type: str, state: str, details?: str|null }[] }`
|
||||||
|
#### Create package for a shipping — `POST /shipping/{id}/package`
|
||||||
|
- Params: `id: int, body: { entityaddressId: int, type: str, state: str, details?: str }`
|
||||||
|
- Returns: `201 { package: { id: int, shippingId: int, entityaddressId: int, number: str, type: str, state: str, details?: str|null } }`
|
||||||
|
|
||||||
|
### Section: /stock
|
||||||
|
|
||||||
|
#### Accept delivery manifest item(s) — `POST /stock/accept`
|
||||||
|
- Params: `body: { manifestarticleId?: int, warehouseId?: int, items?: { manifestarticleId: int, warehouseId?: int }[] }`
|
||||||
|
- Returns: `200`
|
||||||
#### Adjust stock manually — `POST /stock/adjust`
|
#### Adjust stock manually — `POST /stock/adjust`
|
||||||
- Params: `body: { sku?: str, warehouseId?: int, amount?: num, text?: str|null, lines?: { sku: str, amount: num, warehouseId?: int, text?: str }[] }`
|
- Params: `body: { sku?: str, warehouseId?: int, amount?: num, text?: str|null, lines?: { sku: str, amount: num, warehouseId?: int, text?: str }[] }`
|
||||||
- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T14[] }`
|
- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T9[] }`
|
||||||
|
#### Ship shipment manifest item(s) — `POST /stock/ship`
|
||||||
|
- Params: `body: { manifestarticleId?: int, warehouseId?: int, items?: { manifestarticleId: int, warehouseId?: int }[] }`
|
||||||
|
- Returns: `200`
|
||||||
|
|
||||||
|
### Section: /supplier
|
||||||
|
|
||||||
#### List suppliers — `GET /supplier`
|
#### List suppliers — `GET /supplier`
|
||||||
_none_
|
_none_
|
||||||
- Returns: `200 { suppliers: T35[] }`
|
- Returns: `200 { suppliers: T25[] }`
|
||||||
#### Create supplier — `POST /supplier`
|
#### Create supplier — `POST /supplier`
|
||||||
- Params: `body: { name: str }`
|
- Params: `body: { name: str }`
|
||||||
- Returns: `201 { supplier: T35 }`
|
- Returns: `201 { supplier: T25 }`
|
||||||
#### Delete supplier — `DELETE /supplier/{id}`
|
#### Delete supplier — `DELETE /supplier/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
#### Update supplier — `PATCH /supplier/{id}`
|
#### Update supplier — `PATCH /supplier/{id}`
|
||||||
- Params: `id: int, body: { name: str }`
|
- Params: `id: int, body: { name: str }`
|
||||||
- Returns: `200 { supplier: T35 }`
|
- Returns: `200 { supplier: T25 }`
|
||||||
#### List supplier articles by SKUs — `GET /supplier/{id}/articles`
|
#### List supplier articles by SKUs — `GET /supplier/{id}/articles`
|
||||||
- Params: `id: int, query: skus?: string`
|
- Params: `id: int, query: skus?: str`
|
||||||
- Returns: `200 { articles: { id: int, articleId: int, entityId: int, pu: num, sort: int, article: { id: int, sku: str, name?: str|null, slug?: str|null, photos?: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans?: str[] }, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T39|null }[] }`
|
- Returns: `200 { articles: { id: int, articleId: int, entityId: int, pu: num, sort: int, article: { id: int, sku: str, name?: str|null, slug?: str|null, photos?: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans?: str[] }, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T27|null, incoming?: { date: str, amount: num }[] }[] }`
|
||||||
#### Query supplier articles by SKUs (bulk) — `POST /supplier/{id}/articles`
|
#### Query supplier articles by SKUs (bulk) — `POST /supplier/{id}/articles`
|
||||||
- Params: `id: int, body?: { skus?: str[] }`
|
- Params: `id: int, body?: { skus?: str[] }`
|
||||||
- Returns: `200 { articles: { id: int, articleId: int, entityId: int, pu: num, sort: int, article: { id: int, sku: str, name?: str|null, slug?: str|null, photos?: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans?: str[] }, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T39|null }[] }`
|
- Returns: `200 { articles: { id: int, articleId: int, entityId: int, pu: num, sort: int, article: { id: int, sku: str, name?: str|null, slug?: str|null, photos?: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans?: str[] }, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T27|null, incoming?: { date: str, amount: num }[] }[] }`
|
||||||
|
|
||||||
|
### Section: /unit
|
||||||
|
|
||||||
#### Delete unit — `DELETE /unit`
|
#### Delete unit — `DELETE /unit`
|
||||||
- Params: `query: si: str`
|
- Params: `query: si: str`
|
||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
#### List units — `GET /unit`
|
#### List units — `GET /unit`
|
||||||
_none_
|
_none_
|
||||||
- Returns: `200 { units: T30[] }`
|
- Returns: `200 { units: T17[] }`
|
||||||
#### Upsert unit — `PUT /unit`
|
#### Upsert unit — `PUT /unit`
|
||||||
- Params: `body: { si: str, basesi: str, basesifactor: num, symbol: str, name: str }`
|
- Params: `body: { si: str, basesi: str, basesifactor: num, symbol: str, name: str }`
|
||||||
- Returns: `200 { unit: T30 }`
|
- Returns: `200 { unit: T17 }`
|
||||||
|
|
||||||
|
### Section: /warehouse
|
||||||
|
|
||||||
#### List warehouses — `GET /warehouse`
|
#### List warehouses — `GET /warehouse`
|
||||||
_none_
|
_none_
|
||||||
- Returns: `200 { warehouses: T40[] }`
|
- Returns: `200 { warehouses: { id: int, name: str, sort: int }[] }`
|
||||||
#### Create warehouse — `POST /warehouse`
|
#### Create warehouse — `POST /warehouse`
|
||||||
- Params: `body: { name: str, sort?: int }`
|
- Params: `body: { name: str, sort?: int }`
|
||||||
- Returns: `201 T38`
|
- Returns: `201 { warehouse: { id: int, name: str, sort: int } }`
|
||||||
#### Reorder warehouses — `PUT /warehouse/order`
|
#### Reorder warehouses — `PUT /warehouse/order`
|
||||||
- Params: `body: { warehouseIds: int[] }`
|
- Params: `body: { warehouseIds: int[] }`
|
||||||
- Returns: `200 { warehouseIds: int[] }`
|
- Returns: `200 { warehouseIds: int[] }`
|
||||||
@@ -483,24 +704,28 @@ _none_
|
|||||||
- Returns: `204 void`
|
- Returns: `204 void`
|
||||||
#### Get warehouse by ID — `GET /warehouse/{id}`
|
#### Get warehouse by ID — `GET /warehouse/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `200 T38`
|
- Returns: `200 { warehouse: { id: int, name: str, sort: int } }`
|
||||||
#### Update warehouse (partial) — `PATCH /warehouse/{id}`
|
#### Update warehouse (partial) — `PATCH /warehouse/{id}`
|
||||||
- Params: `id: int, body: { name?: str, sort?: int }`
|
- Params: `id: int, body: { name?: str, sort?: int }`
|
||||||
- Returns: `200 T38`
|
- Returns: `200 { warehouse: { id: int, name: str, sort: int } }`
|
||||||
#### Update warehouse — `PUT /warehouse/{id}`
|
#### Update warehouse — `PUT /warehouse/{id}`
|
||||||
- Params: `id: int, body: { name?: str, sort?: int }`
|
- Params: `id: int, body: { name?: str, sort?: int }`
|
||||||
- Returns: `200 T38`
|
- Returns: `200 { warehouse: { id: int, name: str, sort: int } }`
|
||||||
### Binary & streaming routes (reachable only via `fetch()` in javascript_execute)
|
### Binary & streaming routes (reachable only via `fetch()` in javascript_execute)
|
||||||
|
|
||||||
These routes are NOT in the table above because their responses are binary or long-lived streams, which the `dashback_request` tool cannot return usefully. They ARE reachable from inside `javascript_execute` with `fetch("/route")`, where you can decode the bytes, write them to the VFS and embed them in your reply:
|
These routes are NOT in the table above because their payloads (binary request body or binary response) or long-lived streams cannot be handled usefully by the `dashback_request` tool. They ARE reachable from inside `javascript_execute` with `fetch("/route")`:
|
||||||
|
|
||||||
| Method | Route | Returns | Notes |
|
| Method | Route | Returns | Notes |
|
||||||
|:---|:---|:---|:---|
|
|:---|:---|:---|:---|
|
||||||
| `GET` | `/image/{fullHash}` | `image/avif` | Full-size article/brand/category image by SHA-256 hash. Use for inspecting or re-encoding an existing image.
|
| `GET` | `/image/{fullHash}` | `image/avif` | Full-size article/brand/category image by SHA-256 hash. Use for inspecting or re-encoding an existing image.
|
||||||
| `GET` | `/thumb/{fullHash}` | `image/avif` | Thumb AVIF by SHA-256 hash — cheaper than the full image. Takes the same `fullHash` as `/image/{fullHash}`.
|
| `GET` | `/thumb/{fullHash}` | `image/avif` | Thumb AVIF by SHA-256 hash — cheaper than the full image. Takes the same `fullHash` as `/image/{fullHash}`.
|
||||||
| `POST` | `/serper/image` | `image/avif` | Crawl an image from a URL, auto-crop transparent/white borders. Body: `{ url }`.
|
| `POST` | `/serper/image` | `image/avif` | Crawl an image from a URL, auto-crop transparent/white borders. Body: `{ url }`.
|
||||||
|
| `POST` | `/article/{sku}/image` | `201 { image: { picId, sort, full, thumb, hash, thumbHash } }` | Upload full-size AVIF or WebP image for article SKU. WebP is auto-converted to AVIF. Body: raw binary (`image/avif` or `image/webp`).
|
||||||
|
| `POST` | `/attributevalue/{id}/image` | `201 { attributevalue: { id, fullHash, thumbW, thumbH, thumbHash, pics } }` | Upload or replace AVIF/WebP image for attribute value ID. Body: raw binary (`image/avif` or `image/webp`).
|
||||||
|
| `POST` | `/brand/{id}/image` | `201 { brand: { id, fullHash, thumbW, thumbH, thumbHash, pics } }` | Upload or replace AVIF/WebP image for brand ID. Body: raw binary (`image/avif` or `image/webp`).
|
||||||
|
| `POST` | `/category/{id}/image` | `201 { category: { id, thumbW, thumbH, thumbHash } }` | Upload or replace AVIF/WebP image for category ID. Body: raw binary (`image/avif` or `image/webp`).
|
||||||
|
|
||||||
Save the bytes to the VFS, then embed them in your reply as ``:
|
Save downloaded bytes to the VFS, then embed them in your reply as ``:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
module.exports = (async () => {
|
module.exports = (async () => {
|
||||||
@@ -512,4 +737,18 @@ module.exports = (async () => {
|
|||||||
})();
|
})();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Upload an image from the VFS:
|
||||||
|
|
||||||
|
```js
|
||||||
|
module.exports = (async () => {
|
||||||
|
const buf = fs.readFileSync("images/product.avif");
|
||||||
|
const res = await fetch("/article/SKU123/image", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "image/avif" },
|
||||||
|
body: buf,
|
||||||
|
});
|
||||||
|
return await res.json();
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
|
||||||
`fullHash` appears on articles, brands and categories. `thumbHash` is a base64 blur-up placeholder, not a content hash, and is not accepted here.
|
`fullHash` appears on articles, brands and categories. `thumbHash` is a base64 blur-up placeholder, not a content hash, and is not accepted here.
|
||||||
|
|||||||
15
docs/api/routes/.well-known/oauth-authorization-server.md
Normal file
15
docs/api/routes/.well-known/oauth-authorization-server.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# `GET /.well-known/oauth-authorization-server`
|
||||||
|
|
||||||
|
Public. No cookie required.
|
||||||
|
|
||||||
|
OAuth 2.0 Authorization Server Metadata (RFC 8414)
|
||||||
|
|
||||||
|
Returns OAuth 2.0 authorization server endpoints and capabilities.
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Authorization Server Metadata
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
15
docs/api/routes/.well-known/oauth-protected-resource.md
Normal file
15
docs/api/routes/.well-known/oauth-protected-resource.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# `GET /.well-known/oauth-protected-resource`
|
||||||
|
|
||||||
|
Public. No cookie required.
|
||||||
|
|
||||||
|
OAuth 2.0 Protected Resource Metadata (RFC 9728)
|
||||||
|
|
||||||
|
Returns protected resource metadata discovering the authorization server for the MCP endpoint.
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Protected Resource Metadata
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
35
docs/api/routes/account.get.md
Normal file
35
docs/api/routes/account.get.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# `GET /account`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
List accounts
|
||||||
|
|
||||||
|
Lists payment accounts for the selected company.
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
List of accounts
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"accounts": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"companyId": 1,
|
||||||
|
"type": "…",
|
||||||
|
"name": "…"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | No company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
48
docs/api/routes/account.post.md
Normal file
48
docs/api/routes/account.post.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# `POST /account`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Create account
|
||||||
|
|
||||||
|
Creates a payment account for the selected company.
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `type` | yes | string | |
|
||||||
|
| `name` | yes | string | |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "…",
|
||||||
|
"name": "…"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response `201`
|
||||||
|
|
||||||
|
Created account
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"account": {
|
||||||
|
"id": 1,
|
||||||
|
"companyId": 1,
|
||||||
|
"type": "…",
|
||||||
|
"name": "…"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Validation error or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
32
docs/api/routes/account/_id.md
Normal file
32
docs/api/routes/account/_id.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# `DELETE /account/{id}`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Delete account
|
||||||
|
|
||||||
|
Deletes an account of the selected company. Its payments are removed as well.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Account ID |
|
||||||
|
|
||||||
|
## Response `204`
|
||||||
|
|
||||||
|
Account deleted
|
||||||
|
|
||||||
|
_No content_
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
| 404 | Account not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
30
docs/api/routes/agent/events.md
Normal file
30
docs/api/routes/agent/events.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# `GET /agent/events`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Printer agent SSE stream
|
||||||
|
|
||||||
|
Authorized Server-Sent Events stream for connected printer agents.
|
||||||
|
Maintains a persistent connection with regular heartbeats (`: heartbeat\n\n`) and updates `lastseen_at` and `agentversion`.
|
||||||
|
|
||||||
|
|
||||||
|
## Query parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `token` | no | string | Agent auth token if not provided via Authorization header |
|
||||||
|
| `agentversion` | no | string | Current agent version |
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Event stream established
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
31
docs/api/routes/agent/pair.get.md
Normal file
31
docs/api/routes/agent/pair.get.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# `GET /agent/pair`
|
||||||
|
|
||||||
|
Public. No cookie required.
|
||||||
|
|
||||||
|
Pair printer agent via query parameters
|
||||||
|
|
||||||
|
Alternative GET endpoint for devices that pair via query parameters.
|
||||||
|
|
||||||
|
## Query parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `authCode` | no | string | Pairing PIN (or pairingpin) |
|
||||||
|
| `pairingpin` | no | string | |
|
||||||
|
| `serial` | no | string | |
|
||||||
|
| `model` | no | string | |
|
||||||
|
| `version` | no | string | |
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Pairing successful
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Missing or invalid pairing PIN |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
55
docs/api/routes/agent/pair.post.md
Normal file
55
docs/api/routes/agent/pair.post.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# `POST /agent/pair`
|
||||||
|
|
||||||
|
Public. No cookie required.
|
||||||
|
|
||||||
|
Pair printer agent
|
||||||
|
|
||||||
|
Public, unauthorized endpoint used by the printer agent to complete pairing using a 6-digit PIN.
|
||||||
|
Updates `clientserial`, `clientmodel`, and `agentversion`, clears the pairing PIN, and returns a device authentication token.
|
||||||
|
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `pairingpin` | yes | string | |
|
||||||
|
| `clientserial` | no | string | |
|
||||||
|
| `clientmodel` | no | string | |
|
||||||
|
| `agentversion` | no | string | |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"pairingpin": "123456",
|
||||||
|
"clientserial": "SN-987654321",
|
||||||
|
"clientmodel": "Epson TM-T88VI",
|
||||||
|
"agentversion": "1.0.0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Pairing successful
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "…",
|
||||||
|
"companyId": 1,
|
||||||
|
"token": "…",
|
||||||
|
"pairedAt": "…",
|
||||||
|
"clientserial": "…",
|
||||||
|
"clientmodel": "…",
|
||||||
|
"agentversion": "…",
|
||||||
|
"sort": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Missing or invalid pairing PIN — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
53
docs/api/routes/agent/state.md
Normal file
53
docs/api/routes/agent/state.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# `POST /agent/state`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Post agent state
|
||||||
|
|
||||||
|
Authorized endpoint for the printer agent to report its operational status/telemetry.
|
||||||
|
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `state` | no | | Status text or status object |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"state": "…"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
State recorded
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"agent": {
|
||||||
|
"id": 1,
|
||||||
|
"companyId": 1,
|
||||||
|
"name": "…",
|
||||||
|
"token": "…",
|
||||||
|
"pairingpin": "…",
|
||||||
|
"pairedAt": "…",
|
||||||
|
"agentversion": "…",
|
||||||
|
"lastseenAt": "…",
|
||||||
|
"updatedAt": "…",
|
||||||
|
"sort": 1,
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
51
docs/api/routes/agentprinter.md
Normal file
51
docs/api/routes/agentprinter.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# `GET /agentprinter`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
List agent printers
|
||||||
|
|
||||||
|
Lists agent printers for the authenticated user's selected company,
|
||||||
|
optionally filtered by printer agent ID.
|
||||||
|
|
||||||
|
|
||||||
|
## Query parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `printeragentId` | no | integer | Optional printer agent ID filter |
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
List of agent printers
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"agentprinters": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"printeragentId": 1,
|
||||||
|
"type": "…",
|
||||||
|
"model": "…",
|
||||||
|
"serial": "…",
|
||||||
|
"enabled": true,
|
||||||
|
"sort": 1,
|
||||||
|
"open": true,
|
||||||
|
"pause": true,
|
||||||
|
"error": true,
|
||||||
|
"online": true,
|
||||||
|
"labelLength": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | No company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
35
docs/api/routes/agentprinter/_id.delete.md
Normal file
35
docs/api/routes/agentprinter/_id.delete.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# `DELETE /agentprinter/{id}`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Delete agent printer
|
||||||
|
|
||||||
|
Deletes an agent printer from the selected company.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | |
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `agentprinter` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `204`
|
||||||
|
|
||||||
|
Agent printer deleted
|
||||||
|
|
||||||
|
_No content_
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 404 | Agent printer not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
48
docs/api/routes/agentprinter/_id.get.md
Normal file
48
docs/api/routes/agentprinter/_id.get.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# `GET /agentprinter/{id}`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Get agent printer
|
||||||
|
|
||||||
|
Retrieves a single agent printer by ID.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | |
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Agent printer details
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"agentprinter": {
|
||||||
|
"id": 1,
|
||||||
|
"printeragentId": 1,
|
||||||
|
"type": "…",
|
||||||
|
"model": "…",
|
||||||
|
"serial": "…",
|
||||||
|
"enabled": true,
|
||||||
|
"sort": 1,
|
||||||
|
"open": true,
|
||||||
|
"pause": true,
|
||||||
|
"error": true,
|
||||||
|
"online": true,
|
||||||
|
"labelLength": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 404 | Agent printer not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
67
docs/api/routes/agentprinter/_id.patch.md
Normal file
67
docs/api/routes/agentprinter/_id.patch.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# `PATCH /agentprinter/{id}`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Update agent printer
|
||||||
|
|
||||||
|
Updates enabled and/or display order (sort) of an agent printer.
|
||||||
|
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | |
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `enabled` | no | boolean | |
|
||||||
|
| `sort` | no | integer | |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"enabled": true,
|
||||||
|
"sort": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `agentprinter` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Updated agent printer
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"agentprinter": {
|
||||||
|
"id": 1,
|
||||||
|
"printeragentId": 1,
|
||||||
|
"type": "…",
|
||||||
|
"model": "…",
|
||||||
|
"serial": "…",
|
||||||
|
"enabled": true,
|
||||||
|
"sort": 1,
|
||||||
|
"open": true,
|
||||||
|
"pause": true,
|
||||||
|
"error": true,
|
||||||
|
"online": true,
|
||||||
|
"labelLength": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid input or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 404 | Agent printer not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
51
docs/api/routes/agentprinter/order.md
Normal file
51
docs/api/routes/agentprinter/order.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# `PUT /agentprinter/order`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Reorder agent printers
|
||||||
|
|
||||||
|
Reorders agent printers for a specific printer agent within the selected company.
|
||||||
|
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `printeragentId` | no | integer | Optional agent id. If omitted, reorders all printers in the company. |
|
||||||
|
| `agentprinterIds` | yes | integer[] | |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"printeragentId": 1,
|
||||||
|
"agentprinterIds": [
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `agentprinter` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Applied order
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"agentprinterIds": [
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid order or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
@@ -34,13 +34,7 @@ Created child article
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"article": {
|
"sku": "…"
|
||||||
"id": 1,
|
|
||||||
"categoryId": 1,
|
|
||||||
"name": "…",
|
|
||||||
"sku": "…",
|
|
||||||
"track_stock": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -32,17 +32,10 @@ Attaches an existing article to this parent article. Idempotent if already a chi
|
|||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|
||||||
Attached child article and list of children
|
List of children after attaching
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"article": {
|
|
||||||
"id": 1,
|
|
||||||
"categoryId": 1,
|
|
||||||
"name": "…",
|
|
||||||
"sku": "…",
|
|
||||||
"track_stock": true
|
|
||||||
},
|
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
|
|||||||
@@ -34,21 +34,11 @@ when `isParent` is `false`.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Response `200`
|
## Response `204`
|
||||||
|
|
||||||
Updated article
|
Updated
|
||||||
|
|
||||||
```json
|
_No content_
|
||||||
{
|
|
||||||
"article": {
|
|
||||||
"id": 1,
|
|
||||||
"categoryId": 1,
|
|
||||||
"name": "…",
|
|
||||||
"sku": "…",
|
|
||||||
"track_stock": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Errors
|
## Errors
|
||||||
|
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ Prints a label for the article using a label template.
|
|||||||
| Field | Required | Type | Description |
|
| Field | Required | Type | Description |
|
||||||
|-------|----------|------|-------------|
|
|-------|----------|------|-------------|
|
||||||
| `templateId` | yes | integer | |
|
| `templateId` | yes | integer | |
|
||||||
| `printerId` | yes | integer | |
|
| `agentprinterId` | yes | integer | |
|
||||||
| `quantity` | yes | integer | |
|
| `quantity` | yes | integer | |
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"templateId": 1,
|
"templateId": 1,
|
||||||
"printerId": 1,
|
"agentprinterId": 1,
|
||||||
"quantity": 1
|
"quantity": 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -29,84 +29,11 @@ Enables or disables inventory stock tracking for this article.
|
|||||||
|
|
||||||
On success, emits SSE signal: `article-<sku>` (see [`GET /events`](../events.md)).
|
On success, emits SSE signal: `article-<sku>` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `200`
|
## Response `204`
|
||||||
|
|
||||||
Updated article detail
|
Updated
|
||||||
|
|
||||||
```json
|
_No content_
|
||||||
{
|
|
||||||
"article": {
|
|
||||||
"id": 1,
|
|
||||||
"sku": "…",
|
|
||||||
"name": "…",
|
|
||||||
"slug": "…",
|
|
||||||
"desc": "…",
|
|
||||||
"seo": "…",
|
|
||||||
"track_stock": true,
|
|
||||||
"unitamount": 1,
|
|
||||||
"weight": 1,
|
|
||||||
"unit": {
|
|
||||||
"si": "…",
|
|
||||||
"basesi": "…",
|
|
||||||
"basesifactor": 1,
|
|
||||||
"symbol": "…",
|
|
||||||
"name": "…"
|
|
||||||
},
|
|
||||||
"brand": {
|
|
||||||
"id": 1,
|
|
||||||
"name": "…",
|
|
||||||
"fullHash": "…",
|
|
||||||
"thumbW": 1,
|
|
||||||
"thumbH": 1,
|
|
||||||
"thumbHash": "…"
|
|
||||||
},
|
|
||||||
"categoryIds": [
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"photos": [
|
|
||||||
{
|
|
||||||
"sort": 1,
|
|
||||||
"fullHash": "…",
|
|
||||||
"fullW": 1,
|
|
||||||
"fullH": 1,
|
|
||||||
"fullSize": 1,
|
|
||||||
"thumbW": 1,
|
|
||||||
"thumbH": 1,
|
|
||||||
"thumbHash": "…"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"eans": [
|
|
||||||
"…"
|
|
||||||
],
|
|
||||||
"prices": [
|
|
||||||
{
|
|
||||||
"minmultiplier": 1,
|
|
||||||
"net": 1,
|
|
||||||
"taxRate": 1,
|
|
||||||
"customergroupId": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"suppliers": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"articleId": 1,
|
|
||||||
"entityId": 1,
|
|
||||||
"supplierName": "…",
|
|
||||||
"sku": "…",
|
|
||||||
"pu": 1,
|
|
||||||
"stock": 1,
|
|
||||||
"sort": 1,
|
|
||||||
"priceId": 1,
|
|
||||||
"price": {
|
|
||||||
"net": 1,
|
|
||||||
"taxId": 1,
|
|
||||||
"taxRate": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Errors
|
## Errors
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ Creates an article under an authorized category (`parentId`).
|
|||||||
| `name` | yes | string | |
|
| `name` | yes | string | |
|
||||||
| `desc` | no | string | Article description text/HTML |
|
| `desc` | no | string | Article description text/HTML |
|
||||||
| `seo` | no | string | SEO text |
|
| `seo` | no | string | SEO text |
|
||||||
| `track_stock` | no | boolean | Whether to track inventory stock (defaults to true) |
|
| `track_stock` | no | boolean | Whether to track inventory stock (defaults to false) |
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -33,13 +33,7 @@ Created
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"article": {
|
"sku": "…"
|
||||||
"id": 1,
|
|
||||||
"categoryId": 1,
|
|
||||||
"name": "…",
|
|
||||||
"sku": "…",
|
|
||||||
"track_stock": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,22 @@ Article detail
|
|||||||
"net": 1,
|
"net": 1,
|
||||||
"taxId": 1,
|
"taxId": 1,
|
||||||
"taxRate": 1
|
"taxRate": 1
|
||||||
|
},
|
||||||
|
"incoming": [
|
||||||
|
{
|
||||||
|
"date": "2026-08-10T00:00:00.000Z",
|
||||||
|
"amount": 1
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"undeliveredOrders": [
|
||||||
|
{
|
||||||
|
"orderId": 1,
|
||||||
|
"orderNumber": "…",
|
||||||
|
"date": "2026-08-10T00:00:00.000Z",
|
||||||
|
"customerName": "…",
|
||||||
|
"amount": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
52
docs/api/routes/article/history-list.md
Normal file
52
docs/api/routes/article/history-list.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# `GET /article/history`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Query company-wide article history
|
||||||
|
|
||||||
|
Queries article change history across all articles for the authenticated user's selected company.
|
||||||
|
Supports pagination, search filter, and date cutoff via `seekMonths`.
|
||||||
|
|
||||||
|
|
||||||
|
## Query parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `offset` | no | integer | Paging offset |
|
||||||
|
| `limit` | no | integer | Page size |
|
||||||
|
| `search` | no | string | Optional search filter across SKU, article name, and history text |
|
||||||
|
| `seekMonths` | no | integer | Negative integer cutoff months ago (e.g. -13) |
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Paged article history list
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"history": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"date": "2026-08-10T00:00:00.000Z",
|
||||||
|
"sku": "…",
|
||||||
|
"name": "…",
|
||||||
|
"historytext": "…"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 1,
|
||||||
|
"unfilteredTotal": 1,
|
||||||
|
"offset": 1,
|
||||||
|
"limit": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Validation error or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
@@ -5,6 +5,7 @@ Protected. Requires a valid session cookie (`credentials: 'include'`).
|
|||||||
Get article history by SKU
|
Get article history by SKU
|
||||||
|
|
||||||
Returns `articlehistory` rows for the article, newest first (`date` DESC, `id` DESC).
|
Returns `articlehistory` rows for the article, newest first (`date` DESC, `id` DESC).
|
||||||
|
Supports pagination, search filter, and date cutoff via `seekMonths`.
|
||||||
Auth like GET by SKU.
|
Auth like GET by SKU.
|
||||||
|
|
||||||
|
|
||||||
@@ -18,11 +19,14 @@ Auth like GET by SKU.
|
|||||||
|
|
||||||
| Param | Required | Type | Description |
|
| Param | Required | Type | Description |
|
||||||
|-------|----------|------|-------------|
|
|-------|----------|------|-------------|
|
||||||
| `limit` | no | integer | Maximum number of entries to return (newest first). Omit for all entries. |
|
| `offset` | no | integer | Paging offset |
|
||||||
|
| `limit` | no | integer | Page size |
|
||||||
|
| `search` | no | string | Optional search filter for history text |
|
||||||
|
| `seekMonths` | no | integer | Negative integer cutoff months ago (e.g. -13) |
|
||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|
||||||
Article history (newest first)
|
Paged article history list
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -37,7 +41,11 @@ Article history (newest first)
|
|||||||
"date": "2026-08-01T09:00:00.000Z",
|
"date": "2026-08-01T09:00:00.000Z",
|
||||||
"text": "Name geändert von \"\" auf \"Tee\""
|
"text": "Name geändert von \"\" auf \"Tee\""
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"total": 2,
|
||||||
|
"unfilteredTotal": 2,
|
||||||
|
"offset": 0,
|
||||||
|
"limit": 50
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -45,7 +53,7 @@ Article history (newest first)
|
|||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| 400 | Missing sku — `{"error":"…"}` |
|
| 400 | Missing sku or invalid query parameters — `{"error":"…"}` |
|
||||||
| 401 | Unauthorized — `{"error":"…"}` |
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
| 403 | Not authorized — `{"error":"…"}` |
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
| 404 | Article not found — `{"error":"…"}` |
|
| 404 | Article not found — `{"error":"…"}` |
|
||||||
|
|||||||
@@ -30,84 +30,11 @@ Pass `null` to clear the brand. Returns the same shape as GET by SKU.
|
|||||||
|
|
||||||
On success, emits SSE signal: `article-<sku>` (see [`GET /events`](../events.md)).
|
On success, emits SSE signal: `article-<sku>` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `200`
|
## Response `204`
|
||||||
|
|
||||||
Updated article detail
|
Updated
|
||||||
|
|
||||||
```json
|
_No content_
|
||||||
{
|
|
||||||
"article": {
|
|
||||||
"id": 1,
|
|
||||||
"sku": "…",
|
|
||||||
"name": "…",
|
|
||||||
"slug": "…",
|
|
||||||
"desc": "…",
|
|
||||||
"seo": "…",
|
|
||||||
"track_stock": true,
|
|
||||||
"unitamount": 1,
|
|
||||||
"weight": 1,
|
|
||||||
"unit": {
|
|
||||||
"si": "…",
|
|
||||||
"basesi": "…",
|
|
||||||
"basesifactor": 1,
|
|
||||||
"symbol": "…",
|
|
||||||
"name": "…"
|
|
||||||
},
|
|
||||||
"brand": {
|
|
||||||
"id": 1,
|
|
||||||
"name": "…",
|
|
||||||
"fullHash": "…",
|
|
||||||
"thumbW": 1,
|
|
||||||
"thumbH": 1,
|
|
||||||
"thumbHash": "…"
|
|
||||||
},
|
|
||||||
"categoryIds": [
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"photos": [
|
|
||||||
{
|
|
||||||
"sort": 1,
|
|
||||||
"fullHash": "…",
|
|
||||||
"fullW": 1,
|
|
||||||
"fullH": 1,
|
|
||||||
"fullSize": 1,
|
|
||||||
"thumbW": 1,
|
|
||||||
"thumbH": 1,
|
|
||||||
"thumbHash": "…"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"eans": [
|
|
||||||
"…"
|
|
||||||
],
|
|
||||||
"prices": [
|
|
||||||
{
|
|
||||||
"minmultiplier": 1,
|
|
||||||
"net": 1,
|
|
||||||
"taxRate": 1,
|
|
||||||
"customergroupId": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"suppliers": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"articleId": 1,
|
|
||||||
"entityId": 1,
|
|
||||||
"supplierName": "…",
|
|
||||||
"sku": "…",
|
|
||||||
"pu": 1,
|
|
||||||
"stock": 1,
|
|
||||||
"sort": 1,
|
|
||||||
"priceId": 1,
|
|
||||||
"price": {
|
|
||||||
"net": 1,
|
|
||||||
"taxId": 1,
|
|
||||||
"taxRate": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Errors
|
## Errors
|
||||||
|
|
||||||
|
|||||||
@@ -34,84 +34,11 @@ at least one category (empty array is rejected). Returns the same shape as GET b
|
|||||||
|
|
||||||
On success, emits SSE signal: `article-<sku>` (see [`GET /events`](../events.md)).
|
On success, emits SSE signal: `article-<sku>` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `200`
|
## Response `204`
|
||||||
|
|
||||||
Updated article detail
|
Updated
|
||||||
|
|
||||||
```json
|
_No content_
|
||||||
{
|
|
||||||
"article": {
|
|
||||||
"id": 1,
|
|
||||||
"sku": "…",
|
|
||||||
"name": "…",
|
|
||||||
"slug": "…",
|
|
||||||
"desc": "…",
|
|
||||||
"seo": "…",
|
|
||||||
"track_stock": true,
|
|
||||||
"unitamount": 1,
|
|
||||||
"weight": 1,
|
|
||||||
"unit": {
|
|
||||||
"si": "…",
|
|
||||||
"basesi": "…",
|
|
||||||
"basesifactor": 1,
|
|
||||||
"symbol": "…",
|
|
||||||
"name": "…"
|
|
||||||
},
|
|
||||||
"brand": {
|
|
||||||
"id": 1,
|
|
||||||
"name": "…",
|
|
||||||
"fullHash": "…",
|
|
||||||
"thumbW": 1,
|
|
||||||
"thumbH": 1,
|
|
||||||
"thumbHash": "…"
|
|
||||||
},
|
|
||||||
"categoryIds": [
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"photos": [
|
|
||||||
{
|
|
||||||
"sort": 1,
|
|
||||||
"fullHash": "…",
|
|
||||||
"fullW": 1,
|
|
||||||
"fullH": 1,
|
|
||||||
"fullSize": 1,
|
|
||||||
"thumbW": 1,
|
|
||||||
"thumbH": 1,
|
|
||||||
"thumbHash": "…"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"eans": [
|
|
||||||
"…"
|
|
||||||
],
|
|
||||||
"prices": [
|
|
||||||
{
|
|
||||||
"minmultiplier": 1,
|
|
||||||
"net": 1,
|
|
||||||
"taxRate": 1,
|
|
||||||
"customergroupId": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"suppliers": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"articleId": 1,
|
|
||||||
"entityId": 1,
|
|
||||||
"supplierName": "…",
|
|
||||||
"sku": "…",
|
|
||||||
"pu": 1,
|
|
||||||
"stock": 1,
|
|
||||||
"sort": 1,
|
|
||||||
"priceId": 1,
|
|
||||||
"price": {
|
|
||||||
"net": 1,
|
|
||||||
"taxId": 1,
|
|
||||||
"taxRate": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Errors
|
## Errors
|
||||||
|
|
||||||
|
|||||||
@@ -32,84 +32,11 @@ empty array clears all EANs. Returns the same shape as GET by SKU.
|
|||||||
|
|
||||||
On success, emits SSE signal: `article-<sku>` (see [`GET /events`](../events.md)).
|
On success, emits SSE signal: `article-<sku>` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `200`
|
## Response `204`
|
||||||
|
|
||||||
Updated article detail
|
Updated
|
||||||
|
|
||||||
```json
|
_No content_
|
||||||
{
|
|
||||||
"article": {
|
|
||||||
"id": 1,
|
|
||||||
"sku": "…",
|
|
||||||
"name": "…",
|
|
||||||
"slug": "…",
|
|
||||||
"desc": "…",
|
|
||||||
"seo": "…",
|
|
||||||
"track_stock": true,
|
|
||||||
"unitamount": 1,
|
|
||||||
"weight": 1,
|
|
||||||
"unit": {
|
|
||||||
"si": "…",
|
|
||||||
"basesi": "…",
|
|
||||||
"basesifactor": 1,
|
|
||||||
"symbol": "…",
|
|
||||||
"name": "…"
|
|
||||||
},
|
|
||||||
"brand": {
|
|
||||||
"id": 1,
|
|
||||||
"name": "…",
|
|
||||||
"fullHash": "…",
|
|
||||||
"thumbW": 1,
|
|
||||||
"thumbH": 1,
|
|
||||||
"thumbHash": "…"
|
|
||||||
},
|
|
||||||
"categoryIds": [
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"photos": [
|
|
||||||
{
|
|
||||||
"sort": 1,
|
|
||||||
"fullHash": "…",
|
|
||||||
"fullW": 1,
|
|
||||||
"fullH": 1,
|
|
||||||
"fullSize": 1,
|
|
||||||
"thumbW": 1,
|
|
||||||
"thumbH": 1,
|
|
||||||
"thumbHash": "…"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"eans": [
|
|
||||||
"…"
|
|
||||||
],
|
|
||||||
"prices": [
|
|
||||||
{
|
|
||||||
"minmultiplier": 1,
|
|
||||||
"net": 1,
|
|
||||||
"taxRate": 1,
|
|
||||||
"customergroupId": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"suppliers": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"articleId": 1,
|
|
||||||
"entityId": 1,
|
|
||||||
"supplierName": "…",
|
|
||||||
"sku": "…",
|
|
||||||
"pu": 1,
|
|
||||||
"stock": 1,
|
|
||||||
"sort": 1,
|
|
||||||
"priceId": 1,
|
|
||||||
"price": {
|
|
||||||
"net": 1,
|
|
||||||
"taxId": 1,
|
|
||||||
"taxRate": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Errors
|
## Errors
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
Replace POS prices by SKU (net > gross; price=net, tax=taxRate %)
|
Replace POS prices by SKU (net > gross)
|
||||||
|
|
||||||
Replace-all prices for the company POS customer group.
|
Replace-all prices for the company POS customer group.
|
||||||
`net` takes precedence over `price` (legacy alias for net) and `gross`.
|
`net` takes precedence over `gross`.
|
||||||
`tax` and `taxRate` both specify the tax rate percentage (e.g. 19 for 19%), not monetary tax.
|
`taxRate` specifies the tax rate percentage (e.g. 19 for 19%), not monetary tax.
|
||||||
When `gross` is provided, net price is computed as `gross / (1 + taxRate / 100)`.
|
When `gross` is provided, net price is computed as `gross / (1 + taxRate / 100)`.
|
||||||
Prices are strictly scoped to the company's designated POS customer group (customergroupId returned on GET).
|
Prices are strictly scoped to the company's designated POS customer group (customergroupId returned on GET).
|
||||||
Empty `prices` clears all POS prices. Returns the same shape as GET by SKU.
|
Empty `prices` clears all POS prices. Returns the same shape as GET by SKU.
|
||||||
@@ -23,11 +23,9 @@ Empty `prices` clears all POS prices. Returns the same shape as GET by SKU.
|
|||||||
| Field | Required | Type | Description |
|
| Field | Required | Type | Description |
|
||||||
|-------|----------|------|-------------|
|
|-------|----------|------|-------------|
|
||||||
| `prices` | yes | object[] | |
|
| `prices` | yes | object[] | |
|
||||||
| `prices[].net` | no | number | Net price (excl. VAT). Either `net`, `gross`, or legacy `price` is required. |
|
| `prices[].net` | no | number | Net price (excl. VAT). Either `net` or `gross` is required. |
|
||||||
| `prices[].gross` | no | number | Gross price (incl. VAT). Net is computed as `gross / (1 + taxRate / 100)`. |
|
| `prices[].gross` | no | number | Gross price (incl. VAT). Net is computed as `gross / (1 + taxRate / 100)`. |
|
||||||
| `prices[].price` | no | number | Legacy alias for `net`. |
|
|
||||||
| `prices[].taxRate` | no | number | Tax rate percentage (e.g. 19 for 19%, 7 for 7%, 0 for 0%). |
|
| `prices[].taxRate` | no | number | Tax rate percentage (e.g. 19 for 19%, 7 for 7%, 0 for 0%). |
|
||||||
| `prices[].tax` | no | number | Alias for `taxRate` percentage. |
|
|
||||||
| `prices[].minmultiplier` | no | number | Defaults to 1; must be unique per request. |
|
| `prices[].minmultiplier` | no | number | Defaults to 1; must be unique per request. |
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -50,84 +48,11 @@ Empty `prices` clears all POS prices. Returns the same shape as GET by SKU.
|
|||||||
|
|
||||||
On success, emits SSE signal: `article-<sku>` (see [`GET /events`](../events.md)).
|
On success, emits SSE signal: `article-<sku>` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `200`
|
## Response `204`
|
||||||
|
|
||||||
Updated article detail
|
Updated
|
||||||
|
|
||||||
```json
|
_No content_
|
||||||
{
|
|
||||||
"article": {
|
|
||||||
"id": 1,
|
|
||||||
"sku": "…",
|
|
||||||
"name": "…",
|
|
||||||
"slug": "…",
|
|
||||||
"desc": "…",
|
|
||||||
"seo": "…",
|
|
||||||
"track_stock": true,
|
|
||||||
"unitamount": 1,
|
|
||||||
"weight": 1,
|
|
||||||
"unit": {
|
|
||||||
"si": "…",
|
|
||||||
"basesi": "…",
|
|
||||||
"basesifactor": 1,
|
|
||||||
"symbol": "…",
|
|
||||||
"name": "…"
|
|
||||||
},
|
|
||||||
"brand": {
|
|
||||||
"id": 1,
|
|
||||||
"name": "…",
|
|
||||||
"fullHash": "…",
|
|
||||||
"thumbW": 1,
|
|
||||||
"thumbH": 1,
|
|
||||||
"thumbHash": "…"
|
|
||||||
},
|
|
||||||
"categoryIds": [
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"photos": [
|
|
||||||
{
|
|
||||||
"sort": 1,
|
|
||||||
"fullHash": "…",
|
|
||||||
"fullW": 1,
|
|
||||||
"fullH": 1,
|
|
||||||
"fullSize": 1,
|
|
||||||
"thumbW": 1,
|
|
||||||
"thumbH": 1,
|
|
||||||
"thumbHash": "…"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"eans": [
|
|
||||||
"…"
|
|
||||||
],
|
|
||||||
"prices": [
|
|
||||||
{
|
|
||||||
"minmultiplier": 1,
|
|
||||||
"net": 1,
|
|
||||||
"taxRate": 1,
|
|
||||||
"customergroupId": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"suppliers": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"articleId": 1,
|
|
||||||
"entityId": 1,
|
|
||||||
"supplierName": "…",
|
|
||||||
"sku": "…",
|
|
||||||
"pu": 1,
|
|
||||||
"stock": 1,
|
|
||||||
"sort": 1,
|
|
||||||
"priceId": 1,
|
|
||||||
"price": {
|
|
||||||
"net": 1,
|
|
||||||
"taxId": 1,
|
|
||||||
"taxRate": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Errors
|
## Errors
|
||||||
|
|
||||||
|
|||||||
@@ -30,84 +30,11 @@ Returns the same shape as GET by SKU.
|
|||||||
|
|
||||||
On success, emits SSE signal: `article-<sku>` (see [`GET /events`](../events.md)).
|
On success, emits SSE signal: `article-<sku>` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `200`
|
## Response `204`
|
||||||
|
|
||||||
Updated article detail
|
Updated
|
||||||
|
|
||||||
```json
|
_No content_
|
||||||
{
|
|
||||||
"article": {
|
|
||||||
"id": 1,
|
|
||||||
"sku": "…",
|
|
||||||
"name": "…",
|
|
||||||
"slug": "…",
|
|
||||||
"desc": "…",
|
|
||||||
"seo": "…",
|
|
||||||
"track_stock": true,
|
|
||||||
"unitamount": 1,
|
|
||||||
"weight": 1,
|
|
||||||
"unit": {
|
|
||||||
"si": "…",
|
|
||||||
"basesi": "…",
|
|
||||||
"basesifactor": 1,
|
|
||||||
"symbol": "…",
|
|
||||||
"name": "…"
|
|
||||||
},
|
|
||||||
"brand": {
|
|
||||||
"id": 1,
|
|
||||||
"name": "…",
|
|
||||||
"fullHash": "…",
|
|
||||||
"thumbW": 1,
|
|
||||||
"thumbH": 1,
|
|
||||||
"thumbHash": "…"
|
|
||||||
},
|
|
||||||
"categoryIds": [
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"photos": [
|
|
||||||
{
|
|
||||||
"sort": 1,
|
|
||||||
"fullHash": "…",
|
|
||||||
"fullW": 1,
|
|
||||||
"fullH": 1,
|
|
||||||
"fullSize": 1,
|
|
||||||
"thumbW": 1,
|
|
||||||
"thumbH": 1,
|
|
||||||
"thumbHash": "…"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"eans": [
|
|
||||||
"…"
|
|
||||||
],
|
|
||||||
"prices": [
|
|
||||||
{
|
|
||||||
"minmultiplier": 1,
|
|
||||||
"net": 1,
|
|
||||||
"taxRate": 1,
|
|
||||||
"customergroupId": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"suppliers": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"articleId": 1,
|
|
||||||
"entityId": 1,
|
|
||||||
"supplierName": "…",
|
|
||||||
"sku": "…",
|
|
||||||
"pu": 1,
|
|
||||||
"stock": 1,
|
|
||||||
"sort": 1,
|
|
||||||
"priceId": 1,
|
|
||||||
"price": {
|
|
||||||
"net": 1,
|
|
||||||
"taxId": 1,
|
|
||||||
"taxRate": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Errors
|
## Errors
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,13 @@ Created supplier assignment
|
|||||||
"net": 1,
|
"net": 1,
|
||||||
"taxId": 1,
|
"taxId": 1,
|
||||||
"taxRate": 1
|
"taxRate": 1
|
||||||
|
},
|
||||||
|
"incoming": [
|
||||||
|
{
|
||||||
|
"date": "2026-08-10T00:00:00.000Z",
|
||||||
|
"amount": 1
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -33,7 +33,13 @@ List of suppliers linked to the article
|
|||||||
"net": 1,
|
"net": 1,
|
||||||
"taxId": 1,
|
"taxId": 1,
|
||||||
"taxRate": 1
|
"taxRate": 1
|
||||||
|
},
|
||||||
|
"incoming": [
|
||||||
|
{
|
||||||
|
"date": "2026-08-10T00:00:00.000Z",
|
||||||
|
"amount": 1
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,13 @@ Updated supplier assignment
|
|||||||
"net": 1,
|
"net": 1,
|
||||||
"taxId": 1,
|
"taxId": 1,
|
||||||
"taxRate": 1
|
"taxRate": 1
|
||||||
|
},
|
||||||
|
"incoming": [
|
||||||
|
{
|
||||||
|
"date": "2026-08-10T00:00:00.000Z",
|
||||||
|
"amount": 1
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -36,84 +36,11 @@ Patches article fields. At least one of `name`, `unitamount`, `unitSi` required.
|
|||||||
|
|
||||||
On success, emits SSE signal: `articles` (see [`GET /events`](../events.md)).
|
On success, emits SSE signal: `articles` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `200`
|
## Response `204`
|
||||||
|
|
||||||
Updated article detail
|
Updated
|
||||||
|
|
||||||
```json
|
_No content_
|
||||||
{
|
|
||||||
"article": {
|
|
||||||
"id": 1,
|
|
||||||
"sku": "…",
|
|
||||||
"name": "…",
|
|
||||||
"slug": "…",
|
|
||||||
"desc": "…",
|
|
||||||
"seo": "…",
|
|
||||||
"track_stock": true,
|
|
||||||
"unitamount": 1,
|
|
||||||
"weight": 1,
|
|
||||||
"unit": {
|
|
||||||
"si": "…",
|
|
||||||
"basesi": "…",
|
|
||||||
"basesifactor": 1,
|
|
||||||
"symbol": "…",
|
|
||||||
"name": "…"
|
|
||||||
},
|
|
||||||
"brand": {
|
|
||||||
"id": 1,
|
|
||||||
"name": "…",
|
|
||||||
"fullHash": "…",
|
|
||||||
"thumbW": 1,
|
|
||||||
"thumbH": 1,
|
|
||||||
"thumbHash": "…"
|
|
||||||
},
|
|
||||||
"categoryIds": [
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"photos": [
|
|
||||||
{
|
|
||||||
"sort": 1,
|
|
||||||
"fullHash": "…",
|
|
||||||
"fullW": 1,
|
|
||||||
"fullH": 1,
|
|
||||||
"fullSize": 1,
|
|
||||||
"thumbW": 1,
|
|
||||||
"thumbH": 1,
|
|
||||||
"thumbHash": "…"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"eans": [
|
|
||||||
"…"
|
|
||||||
],
|
|
||||||
"prices": [
|
|
||||||
{
|
|
||||||
"minmultiplier": 1,
|
|
||||||
"net": 1,
|
|
||||||
"taxRate": 1,
|
|
||||||
"customergroupId": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"suppliers": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"articleId": 1,
|
|
||||||
"entityId": 1,
|
|
||||||
"supplierName": "…",
|
|
||||||
"sku": "…",
|
|
||||||
"pu": 1,
|
|
||||||
"stock": 1,
|
|
||||||
"sort": 1,
|
|
||||||
"priceId": 1,
|
|
||||||
"price": {
|
|
||||||
"net": 1,
|
|
||||||
"taxId": 1,
|
|
||||||
"taxRate": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Errors
|
## Errors
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Protected. Requires a valid session cookie (`credentials: 'include'`).
|
|||||||
|
|
||||||
List attribute names
|
List attribute names
|
||||||
|
|
||||||
Lists all attribute names defined for the selected company, including nested values.
|
Lists all attribute names defined for the selected company, including nested values. Each attribute name includes `articleCount` (the number of parent articles configured with this attribute) and each value includes `articleCount` (the number of articles having this attribute value).
|
||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|
||||||
@@ -18,12 +18,14 @@ List of attribute names
|
|||||||
"name": "…",
|
"name": "…",
|
||||||
"sort": 1,
|
"sort": 1,
|
||||||
"exclusive": true,
|
"exclusive": true,
|
||||||
|
"articleCount": 1,
|
||||||
"values": [
|
"values": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"attributeId": 1,
|
"attributeId": 1,
|
||||||
"value": "…",
|
"value": "…",
|
||||||
"sort": 1
|
"sort": 1,
|
||||||
|
"articleCount": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ Creates an attribute name for the selected company.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `attributename` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `201`
|
## Response `201`
|
||||||
|
|
||||||
Created attribute name
|
Created attribute name
|
||||||
@@ -33,12 +37,14 @@ Created attribute name
|
|||||||
"name": "…",
|
"name": "…",
|
||||||
"sort": 1,
|
"sort": 1,
|
||||||
"exclusive": true,
|
"exclusive": true,
|
||||||
|
"articleCount": 1,
|
||||||
"values": [
|
"values": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"attributeId": 1,
|
"attributeId": 1,
|
||||||
"value": "…",
|
"value": "…",
|
||||||
"sort": 1
|
"sort": 1,
|
||||||
|
"articleCount": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Protected. Requires a valid session cookie (`credentials: 'include'`).
|
|||||||
|
|
||||||
List values for an attribute
|
List values for an attribute
|
||||||
|
|
||||||
Lists all values for a specific attribute.
|
Lists all values for a specific attribute. Each value includes `articleCount` (the number of articles having this attribute value).
|
||||||
|
|
||||||
## Path parameters
|
## Path parameters
|
||||||
|
|
||||||
@@ -23,7 +23,8 @@ List of attribute values
|
|||||||
"id": 1,
|
"id": 1,
|
||||||
"attributeId": 1,
|
"attributeId": 1,
|
||||||
"value": "…",
|
"value": "…",
|
||||||
"sort": 1
|
"sort": 1,
|
||||||
|
"articleCount": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ Created attribute value
|
|||||||
"id": 1,
|
"id": 1,
|
||||||
"attributeId": 1,
|
"attributeId": 1,
|
||||||
"value": "…",
|
"value": "…",
|
||||||
"sort": 1
|
"sort": 1,
|
||||||
|
"articleCount": 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ Deletes an attribute name and cascades to attribute values and article assignmen
|
|||||||
|-------|----------|------|-------------|
|
|-------|----------|------|-------------|
|
||||||
| `id` | yes | integer | |
|
| `id` | yes | integer | |
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `attributename` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|
||||||
Deleted successfully
|
Deleted successfully
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ Updates name, sort, and/or exclusive flag for an attribute name.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `attributename` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|
||||||
Updated attribute name
|
Updated attribute name
|
||||||
@@ -39,12 +43,14 @@ Updated attribute name
|
|||||||
"name": "…",
|
"name": "…",
|
||||||
"sort": 1,
|
"sort": 1,
|
||||||
"exclusive": true,
|
"exclusive": true,
|
||||||
|
"articleCount": 1,
|
||||||
"values": [
|
"values": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"attributeId": 1,
|
"attributeId": 1,
|
||||||
"value": "…",
|
"value": "…",
|
||||||
"sort": 1
|
"sort": 1,
|
||||||
|
"articleCount": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ Updates the display sort order of attribute names for the selected company.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `attributename` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|
||||||
Reordered successfully
|
Reordered successfully
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ Updated attribute value
|
|||||||
"id": 1,
|
"id": 1,
|
||||||
"attributeId": 1,
|
"attributeId": 1,
|
||||||
"value": "…",
|
"value": "…",
|
||||||
"sort": 1
|
"sort": 1,
|
||||||
|
"articleCount": 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
53
docs/api/routes/attributevalue/_id/image.delete.md
Normal file
53
docs/api/routes/attributevalue/_id/image.delete.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# `DELETE /attributevalue/{id}/image`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Delete attribute value image
|
||||||
|
|
||||||
|
Removes the attribute value's image. Sets thumbW, thumbH, thumbHash back to null. Emits SSE signal `attributename`.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Attribute value id |
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `attributename` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Image removed
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"attributevalue": {
|
||||||
|
"id": 1,
|
||||||
|
"thumbW": 1,
|
||||||
|
"thumbH": 1,
|
||||||
|
"thumbHash": "…",
|
||||||
|
"pics": [
|
||||||
|
{
|
||||||
|
"fullHash": "…",
|
||||||
|
"thumbW": 1,
|
||||||
|
"thumbH": 1,
|
||||||
|
"thumbHash": "…"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | No company selected or invalid id — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
| 404 | Attribute value or image not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
64
docs/api/routes/attributevalue/_id/image.post.md
Normal file
64
docs/api/routes/attributevalue/_id/image.post.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# `POST /attributevalue/{id}/image`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Add or replace attribute value image
|
||||||
|
|
||||||
|
Uploads an image for the attribute value (AVIF, WebP, or octet-stream). Converted to AVIF full and thumbnail.
|
||||||
|
Replaces any existing image for this attribute value. Emits SSE signal `attributename`.
|
||||||
|
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Attribute value id |
|
||||||
|
|
||||||
|
## Body
|
||||||
|
|
||||||
|
Raw binary (`image/avif`).
|
||||||
|
|
||||||
|
Raw binary (`image/webp`).
|
||||||
|
|
||||||
|
Raw binary (`application/octet-stream`).
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `attributename` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `201`
|
||||||
|
|
||||||
|
Image stored and linked
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"attributevalue": {
|
||||||
|
"id": 1,
|
||||||
|
"fullHash": "…",
|
||||||
|
"thumbW": 1,
|
||||||
|
"thumbH": 1,
|
||||||
|
"thumbHash": "…",
|
||||||
|
"pics": [
|
||||||
|
{
|
||||||
|
"fullHash": "…",
|
||||||
|
"thumbW": 1,
|
||||||
|
"thumbH": 1,
|
||||||
|
"thumbHash": "…"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Missing body or invalid image — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
| 404 | Attribute value not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
@@ -4,7 +4,8 @@ Public. No cookie required.
|
|||||||
|
|
||||||
Login with Google ID token
|
Login with Google ID token
|
||||||
|
|
||||||
Verifies a Google ID token, checks allowlist (or creates entry when `IMPLICIT_CREATE=true`),
|
Verifies a Google ID token, checks allowlist (or creates entry when `IMPLICIT_CREATE=true`
|
||||||
|
or when the email has a pending auto-invitation),
|
||||||
stores Google `sub` and profile image bytes, and sets an HTTP-only `token` cookie.
|
stores Google `sub` and profile image bytes, and sets an HTTP-only `token` cookie.
|
||||||
`user.picture` is the profile image as base64 (same as GET /auth/me).
|
`user.picture` is the profile image as base64 (same as GET /auth/me).
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ Login successful; `token` cookie set
|
|||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| 400 | Missing token or Google client not configured — `{"error":"…"}` |
|
| 400 | Missing token or Google client not configured — `{"error":"…"}` |
|
||||||
| 401 | Invalid Google token or account — `{"error":"…"}` |
|
| 401 | Invalid Google token or account — `{"error":"…"}` |
|
||||||
| 403 | Email not on allowlist — `{"error":"…"}` |
|
| 403 | Email not on allowlist or invited — `{"error":"…"}` |
|
||||||
| 500 | Server error — `{"error":"…"}` |
|
| 500 | Server error — `{"error":"…"}` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Current user
|
|||||||
|
|
||||||
Returns the authenticated user from the session cookie and the Google client ID.
|
Returns the authenticated user from the session cookie and the Google client ID.
|
||||||
`user.picture` is the stored profile image as a base64 string when present.
|
`user.picture` is the stored profile image as a base64 string when present.
|
||||||
Unauthenticated calls still receive `googleClientId` with 401.
|
Unauthenticated calls still receive `googleClientId` and `schemaVersion` with 401.
|
||||||
|
|
||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
@@ -21,7 +21,10 @@ Authenticated
|
|||||||
"selectedCompanyId": 1,
|
"selectedCompanyId": 1,
|
||||||
"picture": "…"
|
"picture": "…"
|
||||||
},
|
},
|
||||||
"googleClientId": "…"
|
"googleClientId": "…",
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"vhost": true,
|
||||||
|
"vhostSettings": "…"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -29,7 +32,7 @@ Authenticated
|
|||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| 401 | Unauthorized — still includes `googleClientId` for the frontend |
|
| 401 | Unauthorized — still includes `googleClientId` and `schemaVersion` for the frontend |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ Sets the authenticated user's selected company.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signals: `user`, `company` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|
||||||
Updated user
|
Updated user
|
||||||
@@ -39,6 +43,7 @@ Updated user
|
|||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| 400 | Invalid companyId — `{"error":"…"}` |
|
| 400 | Invalid companyId — `{"error":"…"}` |
|
||||||
| 401 | Unauthorized — `{"error":"…"}` |
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized (the user is not a member of this company) — `{"error":"…"}` |
|
||||||
| 500 | Server error — `{"error":"…"}` |
|
| 500 | Server error — `{"error":"…"}` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
55
docs/api/routes/autoinvite/create.md
Normal file
55
docs/api/routes/autoinvite/create.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# `POST /autoinvite`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Create auto-invitation
|
||||||
|
|
||||||
|
Registers an email for automatic membership. When a user with this email registers,
|
||||||
|
the invitation is removed from the list and the user is added to the company with the given role.
|
||||||
|
Emails that are already registered are rejected (409); use `POST /companyuser` for those. Admin only.
|
||||||
|
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `email` | yes | string | |
|
||||||
|
| `role` | no | string | |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"email": "newhire@example.com",
|
||||||
|
"role": "user"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `autoinvite` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `201`
|
||||||
|
|
||||||
|
Created invitation
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"autoinvite": {
|
||||||
|
"id": 1,
|
||||||
|
"email": "…",
|
||||||
|
"role": "admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | No company selected, invalid email or invalid role — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized (admin of the selected company required) — `{"error":"…"}` |
|
||||||
|
| 409 | User already registered or invitation already exists — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
36
docs/api/routes/autoinvite/delete.md
Normal file
36
docs/api/routes/autoinvite/delete.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# `DELETE /autoinvite/{id}`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Delete auto-invitation
|
||||||
|
|
||||||
|
Deletes a pending auto-invitation of the selected company. Admin only.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | |
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `autoinvite` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `204`
|
||||||
|
|
||||||
|
Deleted
|
||||||
|
|
||||||
|
_No content_
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid id or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized (admin of the selected company required) — `{"error":"…"}` |
|
||||||
|
| 404 | Invitation not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
36
docs/api/routes/autoinvite/list.md
Normal file
36
docs/api/routes/autoinvite/list.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# `GET /autoinvite`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
List auto-invitations
|
||||||
|
|
||||||
|
Lists pending auto-invitations of the selected company. Admin only.
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Pending invitations
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"autoinvites": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"email": "…",
|
||||||
|
"role": "admin",
|
||||||
|
"createdAt": "…"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | No company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized (admin of the selected company required) — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
254
docs/api/routes/backup.md
Normal file
254
docs/api/routes/backup.md
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
# `GET /backup`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
List backups and service status
|
||||||
|
|
||||||
|
Retrieves the current status of the internal backup service and lists available hourly, daily, and monthly backups.
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Backup service status and listing
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": {
|
||||||
|
"enabled": true,
|
||||||
|
"isRunning": true,
|
||||||
|
"dbProvider": "…",
|
||||||
|
"mssqlWarning": true,
|
||||||
|
"backupDir": "…",
|
||||||
|
"retention": {
|
||||||
|
"hourlyMax": 1,
|
||||||
|
"dailyMax": 1,
|
||||||
|
"monthly": "…"
|
||||||
|
},
|
||||||
|
"counts": {
|
||||||
|
"hourly": 1,
|
||||||
|
"daily": 1,
|
||||||
|
"monthly": 1
|
||||||
|
},
|
||||||
|
"latestHourly": {
|
||||||
|
"name": "…",
|
||||||
|
"path": "…",
|
||||||
|
"totalSizeBytes": 1,
|
||||||
|
"timestamp": "2026-08-10T00:00:00.000Z",
|
||||||
|
"manifest": {
|
||||||
|
"id": "…",
|
||||||
|
"timestamp": "2026-08-10T00:00:00.000Z",
|
||||||
|
"dateKey": "…",
|
||||||
|
"monthKey": "…",
|
||||||
|
"dbProvider": "…",
|
||||||
|
"files": {
|
||||||
|
"vfs": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"sourceExists": true
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"sourceExists": true
|
||||||
|
},
|
||||||
|
"database": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"backedUp": true,
|
||||||
|
"reason": "…"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"totalSizeBytes": 1,
|
||||||
|
"durationMs": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"latestDaily": {
|
||||||
|
"name": "…",
|
||||||
|
"path": "…",
|
||||||
|
"totalSizeBytes": 1,
|
||||||
|
"timestamp": "2026-08-10T00:00:00.000Z",
|
||||||
|
"manifest": {
|
||||||
|
"id": "…",
|
||||||
|
"timestamp": "2026-08-10T00:00:00.000Z",
|
||||||
|
"dateKey": "…",
|
||||||
|
"monthKey": "…",
|
||||||
|
"dbProvider": "…",
|
||||||
|
"files": {
|
||||||
|
"vfs": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"sourceExists": true
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"sourceExists": true
|
||||||
|
},
|
||||||
|
"database": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"backedUp": true,
|
||||||
|
"reason": "…"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"totalSizeBytes": 1,
|
||||||
|
"durationMs": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"latestMonthly": {
|
||||||
|
"name": "…",
|
||||||
|
"path": "…",
|
||||||
|
"totalSizeBytes": 1,
|
||||||
|
"timestamp": "2026-08-10T00:00:00.000Z",
|
||||||
|
"manifest": {
|
||||||
|
"id": "…",
|
||||||
|
"timestamp": "2026-08-10T00:00:00.000Z",
|
||||||
|
"dateKey": "…",
|
||||||
|
"monthKey": "…",
|
||||||
|
"dbProvider": "…",
|
||||||
|
"files": {
|
||||||
|
"vfs": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"sourceExists": true
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"sourceExists": true
|
||||||
|
},
|
||||||
|
"database": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"backedUp": true,
|
||||||
|
"reason": "…"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"totalSizeBytes": 1,
|
||||||
|
"durationMs": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"backups": {
|
||||||
|
"counts": {
|
||||||
|
"hourly": 1,
|
||||||
|
"daily": 1,
|
||||||
|
"monthly": 1
|
||||||
|
},
|
||||||
|
"hourly": [
|
||||||
|
{
|
||||||
|
"name": "…",
|
||||||
|
"path": "…",
|
||||||
|
"totalSizeBytes": 1,
|
||||||
|
"timestamp": "2026-08-10T00:00:00.000Z",
|
||||||
|
"manifest": {
|
||||||
|
"id": "…",
|
||||||
|
"timestamp": "2026-08-10T00:00:00.000Z",
|
||||||
|
"dateKey": "…",
|
||||||
|
"monthKey": "…",
|
||||||
|
"dbProvider": "…",
|
||||||
|
"files": {
|
||||||
|
"vfs": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"sourceExists": true
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"sourceExists": true
|
||||||
|
},
|
||||||
|
"database": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"backedUp": true,
|
||||||
|
"reason": "…"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"totalSizeBytes": 1,
|
||||||
|
"durationMs": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"daily": [
|
||||||
|
{
|
||||||
|
"name": "…",
|
||||||
|
"path": "…",
|
||||||
|
"totalSizeBytes": 1,
|
||||||
|
"timestamp": "2026-08-10T00:00:00.000Z",
|
||||||
|
"manifest": {
|
||||||
|
"id": "…",
|
||||||
|
"timestamp": "2026-08-10T00:00:00.000Z",
|
||||||
|
"dateKey": "…",
|
||||||
|
"monthKey": "…",
|
||||||
|
"dbProvider": "…",
|
||||||
|
"files": {
|
||||||
|
"vfs": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"sourceExists": true
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"sourceExists": true
|
||||||
|
},
|
||||||
|
"database": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"backedUp": true,
|
||||||
|
"reason": "…"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"totalSizeBytes": 1,
|
||||||
|
"durationMs": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"monthly": [
|
||||||
|
{
|
||||||
|
"name": "…",
|
||||||
|
"path": "…",
|
||||||
|
"totalSizeBytes": 1,
|
||||||
|
"timestamp": "2026-08-10T00:00:00.000Z",
|
||||||
|
"manifest": {
|
||||||
|
"id": "…",
|
||||||
|
"timestamp": "2026-08-10T00:00:00.000Z",
|
||||||
|
"dateKey": "…",
|
||||||
|
"monthKey": "…",
|
||||||
|
"dbProvider": "…",
|
||||||
|
"files": {
|
||||||
|
"vfs": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"sourceExists": true
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"sourceExists": true
|
||||||
|
},
|
||||||
|
"database": {
|
||||||
|
"name": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"backedUp": true,
|
||||||
|
"reason": "…"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"totalSizeBytes": 1,
|
||||||
|
"durationMs": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 500 | Internal error — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
51
docs/api/routes/backup/_id/vfs.md
Normal file
51
docs/api/routes/backup/_id/vfs.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# `GET /backup/{id}/vfs`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
List VFS files in a backup
|
||||||
|
|
||||||
|
Lists all virtual filesystem (VFS) files stored for the authorized company inside a specific backup archive.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | string | Backup identifier (e.g. backup-2026-10-14_02-00-00, backup-2026-10-14, backup-2026-10) |
|
||||||
|
|
||||||
|
## Query parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `companyId` | no | integer | Optional company ID to inspect (defaults to user's selected company) |
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
List of VFS files in the backup for the authorized company
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"backupId": "…",
|
||||||
|
"companyId": 1,
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"name": "…",
|
||||||
|
"size": 1,
|
||||||
|
"mime": "…",
|
||||||
|
"mtime": "2026-08-10T00:00:00.000Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | No company selected or invalid companyId — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized for the requested company — `{"error":"…"}` |
|
||||||
|
| 404 | Backup or VFS archive not found — `{"error":"…"}` |
|
||||||
|
| 500 | Internal server error — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
55
docs/api/routes/backup/_id/vfs/recover.md
Normal file
55
docs/api/routes/backup/_id/vfs/recover.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# `POST /backup/{id}/vfs/recover`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Recover VFS file from a backup
|
||||||
|
|
||||||
|
Recovers a specific virtual filesystem (VFS) file from a backup archive into the active VFS directory for the authorized company.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | string | Backup identifier (e.g. backup-2026-10-14_02-00-00, backup-2026-10-14, backup-2026-10) |
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `path` | yes | string | Relative path of the VFS file to recover |
|
||||||
|
| `companyId` | no | integer | Optional company ID (defaults to user's selected company) |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"path": "…",
|
||||||
|
"companyId": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
VFS file recovered successfully
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"backupId": "…",
|
||||||
|
"companyId": 1,
|
||||||
|
"path": "…",
|
||||||
|
"sizeBytes": 1,
|
||||||
|
"mime": "…"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Validation error or path escapes boundary — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized for the company — `{"error":"…"}` |
|
||||||
|
| 404 | Backup or file not found — `{"error":"…"}` |
|
||||||
|
| 500 | Internal server error — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
@@ -4,7 +4,7 @@ Protected. Requires a valid session cookie (`credentials: 'include'`).
|
|||||||
|
|
||||||
Delete brand image
|
Delete brand image
|
||||||
|
|
||||||
Removes the brand's image. Sets thumbW, thumbH, thumbHash back to null.
|
Removes the brand's image. Sets thumbW, thumbH, thumbHash back to null. Emits SSE signal `brand` with payload `{ sender, brandId }`.
|
||||||
|
|
||||||
## Path parameters
|
## Path parameters
|
||||||
|
|
||||||
@@ -12,6 +12,10 @@ Removes the brand's image. Sets thumbW, thumbH, thumbHash back to null.
|
|||||||
|-------|----------|------|-------------|
|
|-------|----------|------|-------------|
|
||||||
| `id` | yes | integer | Brand id |
|
| `id` | yes | integer | Brand id |
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `brand` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|
||||||
Image removed
|
Image removed
|
||||||
@@ -25,8 +29,6 @@ Image removed
|
|||||||
"thumbHash": "…",
|
"thumbHash": "…",
|
||||||
"pics": [
|
"pics": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
|
||||||
"sort": 1,
|
|
||||||
"fullHash": "…",
|
"fullHash": "…",
|
||||||
"thumbW": 1,
|
"thumbW": 1,
|
||||||
"thumbH": 1,
|
"thumbH": 1,
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ Protected. Requires a valid session cookie (`credentials: 'include'`).
|
|||||||
Add or replace brand image
|
Add or replace brand image
|
||||||
|
|
||||||
Uploads an image for the brand (AVIF, WebP, or octet-stream). Converted to AVIF full and thumbnail.
|
Uploads an image for the brand (AVIF, WebP, or octet-stream). Converted to AVIF full and thumbnail.
|
||||||
Replaces any existing image for this brand.
|
Replaces any existing image for this brand. Emits SSE signal `brand`
|
||||||
|
with payload `{ sender, brandId }`.
|
||||||
|
|
||||||
|
|
||||||
## Path parameters
|
## Path parameters
|
||||||
@@ -22,6 +23,10 @@ Raw binary (`image/webp`).
|
|||||||
|
|
||||||
Raw binary (`application/octet-stream`).
|
Raw binary (`application/octet-stream`).
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `brand` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `201`
|
## Response `201`
|
||||||
|
|
||||||
Image stored and linked
|
Image stored and linked
|
||||||
@@ -36,8 +41,6 @@ Image stored and linked
|
|||||||
"thumbHash": "…",
|
"thumbHash": "…",
|
||||||
"pics": [
|
"pics": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
|
||||||
"sort": 1,
|
|
||||||
"fullHash": "…",
|
"fullHash": "…",
|
||||||
"thumbW": 1,
|
"thumbW": 1,
|
||||||
"thumbH": 1,
|
"thumbH": 1,
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
# `PUT /brand`
|
# `POST /brand`
|
||||||
|
|
||||||
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
Upsert brand
|
Create brand
|
||||||
|
|
||||||
Insert or update brand by `name` for the selected company. `gsprpinfo` optional.
|
Creates a brand for the selected company. Fails with `Brand already exists`
|
||||||
|
if the name is taken. Emits SSE signal `brand` with payload
|
||||||
|
`{ sender, brandId }` (`sender` echoes the caller's `x-client-id`).
|
||||||
|
|
||||||
|
|
||||||
## Body (JSON)
|
## Body (JSON)
|
||||||
@@ -21,9 +23,13 @@ Insert or update brand by `name` for the selected company. `gsprpinfo` optional.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Response `200`
|
## Signals
|
||||||
|
|
||||||
Upserted brand
|
On success, emits SSE signal: `brand` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `201`
|
||||||
|
|
||||||
|
Created brand
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -33,8 +39,6 @@ Upserted brand
|
|||||||
"gsprpinfo": "…",
|
"gsprpinfo": "…",
|
||||||
"pics": [
|
"pics": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
|
||||||
"sort": 1,
|
|
||||||
"fullHash": "…",
|
"fullHash": "…",
|
||||||
"thumbW": 1,
|
"thumbW": 1,
|
||||||
"thumbH": 1,
|
"thumbH": 1,
|
||||||
@@ -53,6 +57,7 @@ Upserted brand
|
|||||||
| 401 | Unauthorized — `{"error":"…"}` |
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
| 403 | Not authorized — `{"error":"…"}` |
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
| 404 | Company not found — `{"error":"…"}` |
|
| 404 | Company not found — `{"error":"…"}` |
|
||||||
|
| 409 | Brand already exists — `{"error":"…"}` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -5,7 +5,8 @@ Protected. Requires a valid session cookie (`credentials: 'include'`).
|
|||||||
Delete brand
|
Delete brand
|
||||||
|
|
||||||
Deletes the brand for the selected company. Fails with `Brand in use`
|
Deletes the brand for the selected company. Fails with `Brand in use`
|
||||||
if any article still references it.
|
if any article still references it. Emits SSE signal `brand`
|
||||||
|
with payload `{ sender, brandId }`.
|
||||||
|
|
||||||
|
|
||||||
## Path parameters
|
## Path parameters
|
||||||
@@ -14,6 +15,10 @@ if any article still references it.
|
|||||||
|-------|----------|------|-------------|
|
|-------|----------|------|-------------|
|
||||||
| `id` | yes | integer | |
|
| `id` | yes | integer | |
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `brand` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `204`
|
## Response `204`
|
||||||
|
|
||||||
Deleted
|
Deleted
|
||||||
|
|||||||
@@ -4,7 +4,15 @@ Protected. Requires a valid session cookie (`credentials: 'include'`).
|
|||||||
|
|
||||||
List brands
|
List brands
|
||||||
|
|
||||||
Lists brands for the authenticated user's selected company, sorted by name.
|
Lists brands for the authenticated user's selected company, sorted by name. Paged; `total` is the unpaged count. Each brand includes `articleCount` (child articles inherit the brand from their father article; father articles are not counted). Small-thumbnail fields are omitted by default. Set `includeSmallThumbs=true` to include each picture's `smallThumbW`, `smallThumbH`, and `smallThumbBinary` (base64-encoded AVIF bytes, resized with a 32px maximum edge).
|
||||||
|
|
||||||
|
## Query parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `offset` | no | integer | Number of brands to skip |
|
||||||
|
| `limit` | no | integer | Number of brands to return |
|
||||||
|
| `includeSmallThumbs` | no | boolean | Include each picture's small thumbnail dimensions and base64 AVIF bytes. |
|
||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|
||||||
@@ -16,19 +24,24 @@ Brand list
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "…",
|
"name": "…",
|
||||||
|
"articleCount": 1,
|
||||||
"gsprpinfo": "…",
|
"gsprpinfo": "…",
|
||||||
"pics": [
|
"pics": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
|
||||||
"sort": 1,
|
|
||||||
"fullHash": "…",
|
"fullHash": "…",
|
||||||
"thumbW": 1,
|
"thumbW": 1,
|
||||||
"thumbH": 1,
|
"thumbH": 1,
|
||||||
"thumbHash": "…"
|
"thumbHash": "…",
|
||||||
|
"smallThumbW": 1,
|
||||||
|
"smallThumbH": 1,
|
||||||
|
"smallThumbBinary": "…"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"total": 1,
|
||||||
|
"offset": 1,
|
||||||
|
"limit": 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -36,7 +49,7 @@ Brand list
|
|||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| 400 | No company selected — `{"error":"…"}` |
|
| 400 | Invalid offset, invalid limit, invalid includeSmallThumbs, or no company selected — `{"error":"…"}` |
|
||||||
| 401 | Unauthorized — `{"error":"…"}` |
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
| 403 | Not authorized — `{"error":"…"}` |
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
| 404 | Company not found — `{"error":"…"}` |
|
| 404 | Company not found — `{"error":"…"}` |
|
||||||
|
|||||||
65
docs/api/routes/brand/update.md
Normal file
65
docs/api/routes/brand/update.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# `PATCH /brand/{id}`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Rename brand
|
||||||
|
|
||||||
|
Renames/updates a brand by id for the selected company. Fails with
|
||||||
|
`Brand already exists` if the name is taken by another brand.
|
||||||
|
When the name changes, linked articles (`brand_id`) get their
|
||||||
|
`updated_at` bumped; the response reports `touchedArticleCount`.
|
||||||
|
Emits SSE signal `brand` with payload `{ sender, brandId }`
|
||||||
|
(`sender` echoes the caller's `x-client-id`).
|
||||||
|
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | |
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `name` | no | string | |
|
||||||
|
| `gsprpinfo` | no | string | |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Acme GmbH"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `brand` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Updated brand
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"brand": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "…",
|
||||||
|
"gsprpinfo": "…",
|
||||||
|
"touchedArticleCount": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid brand, invalid id, or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
| 404 | Brand or company not found — `{"error":"…"}` |
|
||||||
|
| 409 | Brand already exists — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
37
docs/api/routes/carrier.get.md
Normal file
37
docs/api/routes/carrier.get.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# `GET /carrier`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
List carriers
|
||||||
|
|
||||||
|
Lists carriers for the selected company, ordered by sort and name.
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
List of carriers
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"carriers": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "…",
|
||||||
|
"sort": 1,
|
||||||
|
"hasPackages": true,
|
||||||
|
"isReverse": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | No company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
60
docs/api/routes/carrier.post.md
Normal file
60
docs/api/routes/carrier.post.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# `POST /carrier`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Create carrier
|
||||||
|
|
||||||
|
Creates a new carrier for the selected company.
|
||||||
|
Sort order will be automatically appended if omitted.
|
||||||
|
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `name` | yes | string | |
|
||||||
|
| `sort` | no | integer | Optional sort order |
|
||||||
|
| `hasPackages` | no | boolean | |
|
||||||
|
| `isReverse` | no | boolean | |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "…",
|
||||||
|
"sort": 1,
|
||||||
|
"hasPackages": true,
|
||||||
|
"isReverse": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `carrier` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `201`
|
||||||
|
|
||||||
|
Created carrier
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"carrier": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "…",
|
||||||
|
"sort": 1,
|
||||||
|
"hasPackages": true,
|
||||||
|
"isReverse": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid input or missing fields — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
| 409 | Carrier already exists — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
37
docs/api/routes/carrier/_id.delete.md
Normal file
37
docs/api/routes/carrier/_id.delete.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# `DELETE /carrier/{id}`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Delete carrier
|
||||||
|
|
||||||
|
Deletes a carrier for the selected company, unless referenced in shipping.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Carrier ID |
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `carrier` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `204`
|
||||||
|
|
||||||
|
Carrier deleted
|
||||||
|
|
||||||
|
_No content_
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
| 404 | Carrier not found — `{"error":"…"}` |
|
||||||
|
| 409 | Carrier in use in shipping — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
42
docs/api/routes/carrier/_id.get.md
Normal file
42
docs/api/routes/carrier/_id.get.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# `GET /carrier/{id}`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Get carrier by ID
|
||||||
|
|
||||||
|
Retrieves a single carrier for the authenticated user's selected company.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Carrier ID |
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Carrier details
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"carrier": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "…",
|
||||||
|
"sort": 1,
|
||||||
|
"hasPackages": true,
|
||||||
|
"isReverse": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
| 404 | Carrier not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
65
docs/api/routes/carrier/_id.patch.md
Normal file
65
docs/api/routes/carrier/_id.patch.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# `PATCH /carrier/{id}`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Update carrier
|
||||||
|
|
||||||
|
Updates carrier name, sort order, hasPackages, and/or isReverse for the selected company.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Carrier ID |
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `name` | no | string | |
|
||||||
|
| `sort` | no | integer | |
|
||||||
|
| `hasPackages` | no | boolean | |
|
||||||
|
| `isReverse` | no | boolean | |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "…",
|
||||||
|
"sort": 1,
|
||||||
|
"hasPackages": true,
|
||||||
|
"isReverse": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `carrier` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Updated carrier
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"carrier": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "…",
|
||||||
|
"sort": 1,
|
||||||
|
"hasPackages": true,
|
||||||
|
"isReverse": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid input or missing fields — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
| 404 | Carrier not found — `{"error":"…"}` |
|
||||||
|
| 409 | Carrier already exists — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
51
docs/api/routes/carrier/order.md
Normal file
51
docs/api/routes/carrier/order.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# `PUT /carrier/order`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Reorder carriers
|
||||||
|
|
||||||
|
Reorders carriers for the selected company.
|
||||||
|
`carrierIds` must include all existing carrier IDs exactly once.
|
||||||
|
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `carrierIds` | yes | integer[] | |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"carrierIds": [
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `carrier` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Applied carrier order
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"carrierIds": [
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid carrier order or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
64
docs/api/routes/category/_id/image/generate.md
Normal file
64
docs/api/routes/category/_id/image/generate.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# `POST /category/{id}/image/generate`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Generate category mosaic image from contents
|
||||||
|
|
||||||
|
Generates a 2x2, 3x3, or 4x4 mosaic image from the category's and its subcategories'
|
||||||
|
articles (grouped by subcategory) and sets it as the new main picture for the category.
|
||||||
|
If size is not specified, defaults to 3 (3x3), falling back to 2 (2x2) if fewer than 7 images are available.
|
||||||
|
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Category id |
|
||||||
|
|
||||||
|
## Body (JSON) — _optional_
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `size` | no | integer | Mosaic grid size (2 for 2x2, 3 for 3x3, 4 for 4x4). Defaults to 3x3 (or 2x2 if < 7 images). |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"size": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `category` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Mosaic image generated and linked as category main picture
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"category": {
|
||||||
|
"id": 1,
|
||||||
|
"fullHash": "…",
|
||||||
|
"thumbW": 1,
|
||||||
|
"thumbH": 1,
|
||||||
|
"thumbHash": "…",
|
||||||
|
"size": 1,
|
||||||
|
"grid": "…",
|
||||||
|
"articleCount": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid category ID or size — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
| 404 | Category not found or no article images found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
@@ -4,7 +4,7 @@ Protected. Requires a valid session cookie (`credentials: 'include'`).
|
|||||||
|
|
||||||
Articles in category
|
Articles in category
|
||||||
|
|
||||||
Returns articles in a category the user is authorized for (omits child articles of a parent article), sorted alphabetically by name. Each article includes `childCount` (the number of its child articles).
|
Returns articles in a category the user is authorized for, sorted alphabetically by name. By default includes child articles (variants); pass `includeChildren=false` to omit them. Each article includes `childCount` (the number of its child articles) and small thumbnail fields (`smallThumbW`, `smallThumbH`, `smallThumbBinary`).
|
||||||
|
|
||||||
## Path parameters
|
## Path parameters
|
||||||
|
|
||||||
@@ -12,6 +12,12 @@ Returns articles in a category the user is authorized for (omits child articles
|
|||||||
|-------|----------|------|-------------|
|
|-------|----------|------|-------------|
|
||||||
| `id` | yes | integer | Category id |
|
| `id` | yes | integer | Category id |
|
||||||
|
|
||||||
|
## Query parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `includeChildren` | no | boolean | When true (default), includes child articles (variants) alongside parent articles. Set to false to omit child articles. |
|
||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|
||||||
Articles list
|
Articles list
|
||||||
@@ -36,6 +42,9 @@ Articles list
|
|||||||
"thumbW": 1,
|
"thumbW": 1,
|
||||||
"thumbH": 1,
|
"thumbH": 1,
|
||||||
"thumbHash": "…",
|
"thumbHash": "…",
|
||||||
|
"smallThumbW": 1,
|
||||||
|
"smallThumbH": 1,
|
||||||
|
"smallThumbBinary": "…",
|
||||||
"brand": {
|
"brand": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "…",
|
"name": "…",
|
||||||
@@ -43,7 +52,25 @@ Articles list
|
|||||||
"thumbW": 1,
|
"thumbW": 1,
|
||||||
"thumbH": 1,
|
"thumbH": 1,
|
||||||
"thumbHash": "…"
|
"thumbHash": "…"
|
||||||
|
},
|
||||||
|
"articlewarehousesummary": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"articleId": 1,
|
||||||
|
"article_id": 1,
|
||||||
|
"warehouseId": 1,
|
||||||
|
"warehouse_id": 1,
|
||||||
|
"warehouseName": "…",
|
||||||
|
"stock": 1
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"warehouses": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "…",
|
||||||
|
"stock": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Protected. Requires a valid session cookie (`credentials: 'include'`).
|
|||||||
|
|
||||||
Delete category
|
Delete category
|
||||||
|
|
||||||
Deletes a category the user is authorized for (not the company root).
|
Deletes a category the user is authorized for (not the company root). When recursive=true, recursively deletes all child categories, unassigns POS terminals, and deletes articles that only exist in the deleted subtree (including their children).
|
||||||
|
|
||||||
## Path parameters
|
## Path parameters
|
||||||
|
|
||||||
@@ -12,9 +12,15 @@ Deletes a category the user is authorized for (not the company root).
|
|||||||
|-------|----------|------|-------------|
|
|-------|----------|------|-------------|
|
||||||
| `id` | yes | integer | Category id |
|
| `id` | yes | integer | Category id |
|
||||||
|
|
||||||
|
## Query parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `recursive` | no | boolean | When true, recursively deletes the category subtree and articles that only exist within it. |
|
||||||
|
|
||||||
## Signals
|
## Signals
|
||||||
|
|
||||||
On success, emits SSE signal: `category` (see [`GET /events`](../events.md)).
|
On success, emits SSE signals: `category`, `articles` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `204`
|
## Response `204`
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Returns the category tree for the authenticated user's selected company.
|
|||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|
||||||
Tree and articles for the selected company
|
Tree and article count for the selected company
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -24,39 +24,13 @@ Tree and articles for the selected company
|
|||||||
"thumbW": 1,
|
"thumbW": 1,
|
||||||
"thumbH": 1,
|
"thumbH": 1,
|
||||||
"thumbHash": "…",
|
"thumbHash": "…",
|
||||||
|
"articleCount": 1,
|
||||||
"posterminalIds": [
|
"posterminalIds": [
|
||||||
1
|
1
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"articles": [
|
"articleCount": 1
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"sku": "…",
|
|
||||||
"name": "…",
|
|
||||||
"slug": "…",
|
|
||||||
"net": 1,
|
|
||||||
"taxRate": 1,
|
|
||||||
"track_stock": true,
|
|
||||||
"optionsCount": 1,
|
|
||||||
"articleListCount": 1,
|
|
||||||
"childCount": 1,
|
|
||||||
"parentArticleId": 1,
|
|
||||||
"parentSku": "…",
|
|
||||||
"fullHash": "…",
|
|
||||||
"thumbW": 1,
|
|
||||||
"thumbH": 1,
|
|
||||||
"thumbHash": "…",
|
|
||||||
"brand": {
|
|
||||||
"id": 1,
|
|
||||||
"name": "…",
|
|
||||||
"fullHash": "…",
|
|
||||||
"thumbW": 1,
|
|
||||||
"thumbH": 1,
|
|
||||||
"thumbHash": "…"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ Creates a company and selects it for the current user.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signals: `user`, `company` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `201`
|
## Response `201`
|
||||||
|
|
||||||
Created; selected company updated
|
Created; selected company updated
|
||||||
@@ -32,10 +36,14 @@ Created; selected company updated
|
|||||||
{
|
{
|
||||||
"company": {
|
"company": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"iso639": "…",
|
|
||||||
"name": "…",
|
"name": "…",
|
||||||
|
"rootCategoryId": 1,
|
||||||
|
"languageId": 1,
|
||||||
|
"iso639": "…",
|
||||||
"taxrate": 1,
|
"taxrate": 1,
|
||||||
"reducedtaxrate": 1
|
"reducedtaxrate": 1,
|
||||||
|
"taxfreerate": 1,
|
||||||
|
"role": "admin"
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
@@ -52,7 +60,7 @@ Created; selected company updated
|
|||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| 400 | Validation error — `{"error":"…"}` |
|
| 400 | Validation error — `{"error":"…"}` |
|
||||||
| 401 | Unauthorized — `{"error":"…"}` |
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
| 409 | Company name not available — `{"error":"…"}` |
|
| 403 | Not authorized (invited users cannot create companies when implicit create is disabled) — `{"error":"…"}` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ Deletes a company.
|
|||||||
|-------|----------|------|-------------|
|
|-------|----------|------|-------------|
|
||||||
| `id` | yes | integer | |
|
| `id` | yes | integer | |
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signals: `user`, `company` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `204`
|
## Response `204`
|
||||||
|
|
||||||
Deleted
|
Deleted
|
||||||
|
|||||||
33
docs/api/routes/company/export.md
Normal file
33
docs/api/routes/company/export.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# `GET /company/export`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Export company catalog as zip archive (SSE)
|
||||||
|
|
||||||
|
Exports full company catalog data (categories hierarchy, articles, variants, brands, pictures)
|
||||||
|
into a round-trip compatible .zip archive matching the format of `/company/import`.
|
||||||
|
Streams progress events via Server-Sent Events (`text/event-stream`), followed by base64-encoded
|
||||||
|
binary chunks in `chunk` events and a final `done` event.
|
||||||
|
|
||||||
|
|
||||||
|
## Query parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `stream` | no | string | Force SSE stream mode (`1`) or direct binary download (`0`) |
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Export progress stream (`text/event-stream`) with binary chunks, or raw ZIP file
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Bad request (no company selected) — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 500 | Server error — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
23
docs/api/routes/company/export.post.md
Normal file
23
docs/api/routes/company/export.post.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# `POST /company/export`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Export company catalog as zip archive (SSE)
|
||||||
|
|
||||||
|
POST variant of `/company/export` with identical SSE streaming semantics.
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Export progress stream (`text/event-stream`) with binary chunks, or raw ZIP file
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Bad request (no company selected) — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 500 | Server error — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
@@ -15,10 +15,14 @@ Company list
|
|||||||
"companies": [
|
"companies": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"iso639": "…",
|
|
||||||
"name": "…",
|
"name": "…",
|
||||||
|
"rootCategoryId": 1,
|
||||||
|
"languageId": 1,
|
||||||
|
"iso639": "…",
|
||||||
"taxrate": 1,
|
"taxrate": 1,
|
||||||
"reducedtaxrate": 1
|
"reducedtaxrate": 1,
|
||||||
|
"taxfreerate": 1,
|
||||||
|
"role": "admin"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ Updates a company.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `company` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|
||||||
Updated
|
Updated
|
||||||
@@ -38,10 +42,14 @@ Updated
|
|||||||
{
|
{
|
||||||
"company": {
|
"company": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"iso639": "…",
|
|
||||||
"name": "…",
|
"name": "…",
|
||||||
|
"rootCategoryId": 1,
|
||||||
|
"languageId": 1,
|
||||||
|
"iso639": "…",
|
||||||
"taxrate": 1,
|
"taxrate": 1,
|
||||||
"reducedtaxrate": 1
|
"reducedtaxrate": 1,
|
||||||
|
"taxfreerate": 1,
|
||||||
|
"role": "admin"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -53,7 +61,6 @@ Updated
|
|||||||
| 400 | Validation error — `{"error":"…"}` |
|
| 400 | Validation error — `{"error":"…"}` |
|
||||||
| 401 | Unauthorized — `{"error":"…"}` |
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
| 404 | Company not found — `{"error":"…"}` |
|
| 404 | Company not found — `{"error":"…"}` |
|
||||||
| 409 | Company name not available — `{"error":"…"}` |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
58
docs/api/routes/companyuser/create.md
Normal file
58
docs/api/routes/companyuser/create.md
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# `POST /companyuser`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Add an existing user to the company
|
||||||
|
|
||||||
|
Adds an existing (registered) user, identified by email, to the selected company. Admin only.
|
||||||
|
The added user can then switch to this company via `PUT /auth/selected-company`.
|
||||||
|
For emails that are not registered yet use `POST /autoinvite`.
|
||||||
|
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `email` | yes | string | |
|
||||||
|
| `role` | no | string | |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"email": "colleague@example.com",
|
||||||
|
"role": "user"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `companyuser` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `201`
|
||||||
|
|
||||||
|
Created company user
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"companyuser": {
|
||||||
|
"id": 1,
|
||||||
|
"googleAuthId": 1,
|
||||||
|
"email": "…",
|
||||||
|
"picture": "…",
|
||||||
|
"role": "admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | No company selected, invalid email or invalid role — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized (admin of the selected company required) — `{"error":"…"}` |
|
||||||
|
| 404 | No registered user with this email — `{"error":"…"}` |
|
||||||
|
| 409 | User already in company — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
40
docs/api/routes/companyuser/delete.md
Normal file
40
docs/api/routes/companyuser/delete.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# `DELETE /companyuser/{id}`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Remove a user from the company
|
||||||
|
|
||||||
|
Removes a member from the selected company. History entries stay but lose their author link;
|
||||||
|
if the removed user had this company selected, their selection is cleared.
|
||||||
|
The last admin cannot be removed. Admin only.
|
||||||
|
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | |
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `companyuser` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `204`
|
||||||
|
|
||||||
|
Removed
|
||||||
|
|
||||||
|
_No content_
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid id or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized (admin of the selected company required) — `{"error":"…"}` |
|
||||||
|
| 404 | Company user not found — `{"error":"…"}` |
|
||||||
|
| 409 | Cannot remove the last admin — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
37
docs/api/routes/companyuser/list.md
Normal file
37
docs/api/routes/companyuser/list.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# `GET /companyuser`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
List company users
|
||||||
|
|
||||||
|
Lists the members of the selected company with their role. Admin only.
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Company members
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"companyusers": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"googleAuthId": 1,
|
||||||
|
"email": "…",
|
||||||
|
"picture": "…",
|
||||||
|
"role": "admin"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | No company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized (admin of the selected company required) — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
59
docs/api/routes/companyuser/update.md
Normal file
59
docs/api/routes/companyuser/update.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# `PATCH /companyuser/{id}`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Change a company user's role
|
||||||
|
|
||||||
|
Changes the role of a member of the selected company. The last admin cannot be demoted. Admin only.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | |
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `role` | yes | string | |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"role": "admin"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `companyuser` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
Updated company user
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"companyuser": {
|
||||||
|
"id": 1,
|
||||||
|
"googleAuthId": 1,
|
||||||
|
"email": "…",
|
||||||
|
"picture": "…",
|
||||||
|
"role": "admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid id, role or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized (admin of the selected company required) — `{"error":"…"}` |
|
||||||
|
| 404 | Company user not found — `{"error":"…"}` |
|
||||||
|
| 409 | Cannot demote the last admin — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
38
docs/api/routes/delivery/_id/file/_fileId.delete.md
Normal file
38
docs/api/routes/delivery/_id/file/_fileId.delete.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# `DELETE /delivery/{id}/file/{fileId}`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Delete delivery file attachment
|
||||||
|
|
||||||
|
Removes a file attachment from the delivery and deletes the underlying file.
|
||||||
|
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Delivery ID |
|
||||||
|
| `fileId` | yes | integer | File ID or attachment ID |
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `delivery` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `204`
|
||||||
|
|
||||||
|
Attachment deleted
|
||||||
|
|
||||||
|
_No content_
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid parameters or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
| 404 | Delivery or attachment not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
58
docs/api/routes/delivery/_id/pdf.md
Normal file
58
docs/api/routes/delivery/_id/pdf.md
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# `POST /delivery/{id}/pdf`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Generate and attach delivery note PDF (goods receipt)
|
||||||
|
|
||||||
|
Generates an A4 PDF document for the goods receipt / delivery note with line items and quantities.
|
||||||
|
Attaches the PDF as a `manifestattachment` to the delivery.
|
||||||
|
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Delivery ID |
|
||||||
|
|
||||||
|
## Signals
|
||||||
|
|
||||||
|
On success, emits SSE signal: `delivery` (see [`GET /events`](../events.md)).
|
||||||
|
|
||||||
|
## Response `201`
|
||||||
|
|
||||||
|
PDF generated and attached to delivery
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"attachment": {
|
||||||
|
"id": 1,
|
||||||
|
"manifestId": 1,
|
||||||
|
"fileId": 1,
|
||||||
|
"sort": 1,
|
||||||
|
"hash": "…",
|
||||||
|
"mimetype": "…",
|
||||||
|
"name": "…",
|
||||||
|
"size": 1,
|
||||||
|
"created": "2026-08-10T00:00:00.000Z",
|
||||||
|
"modified": "2026-08-10T00:00:00.000Z",
|
||||||
|
"skeledash": "…",
|
||||||
|
"thumbx": 1,
|
||||||
|
"thumby": 1,
|
||||||
|
"meta": {}
|
||||||
|
},
|
||||||
|
"fileUrl": "/delivery/1/file/5"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
| 404 | Delivery not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
@@ -17,7 +17,7 @@ The manifest `number` is allocated automatically via
|
|||||||
|-------|----------|------|-------------|
|
|-------|----------|------|-------------|
|
||||||
| `externalNumber` | yes | string | User-supplied reference number |
|
| `externalNumber` | yes | string | User-supplied reference number |
|
||||||
| `date` | yes | string | |
|
| `date` | yes | string | |
|
||||||
| `supplierId` | no | integer | |
|
| `supplierId` | yes | integer | |
|
||||||
| `lines` | yes | DeliveryLineInput[] | |
|
| `lines` | yes | DeliveryLineInput[] | |
|
||||||
| `files` | no | DeliveryFileInput[] | Attached documents (pdf or txt) stored with the delivery and returned in `files` on every delivery response. Thumbnails (AVIF) are generated server-side. |
|
| `files` | no | DeliveryFileInput[] | Attached documents (pdf or txt) stored with the delivery and returned in `files` on every delivery response. Thumbnails (AVIF) are generated server-side. |
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ Created delivery
|
|||||||
"size": 1,
|
"size": 1,
|
||||||
"created": "2026-08-10T00:00:00.000Z",
|
"created": "2026-08-10T00:00:00.000Z",
|
||||||
"modified": "2026-08-10T00:00:00.000Z",
|
"modified": "2026-08-10T00:00:00.000Z",
|
||||||
"thumbavif": "…",
|
"skeledash": "…",
|
||||||
"thumbx": 1,
|
"thumbx": 1,
|
||||||
"thumby": 1,
|
"thumby": 1,
|
||||||
"meta": {}
|
"meta": {}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ Delivery
|
|||||||
"size": 1,
|
"size": 1,
|
||||||
"created": "2026-08-10T00:00:00.000Z",
|
"created": "2026-08-10T00:00:00.000Z",
|
||||||
"modified": "2026-08-10T00:00:00.000Z",
|
"modified": "2026-08-10T00:00:00.000Z",
|
||||||
"thumbavif": "…",
|
"skeledash": "…",
|
||||||
"thumbx": 1,
|
"thumbx": 1,
|
||||||
"thumby": 1,
|
"thumby": 1,
|
||||||
"meta": {}
|
"meta": {}
|
||||||
|
|||||||
@@ -5,12 +5,23 @@ Protected. Requires a valid session cookie (`credentials: 'include'`).
|
|||||||
List deliveries
|
List deliveries
|
||||||
|
|
||||||
Lists goods receipts (`manifest` rows with `type = 'delivery'`)
|
Lists goods receipts (`manifest` rows with `type = 'delivery'`)
|
||||||
for the authenticated user's selected company.
|
for the authenticated user's selected company. Supports pagination and filtering.
|
||||||
|
|
||||||
|
|
||||||
|
## Query parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `offset` | no | integer | Number of records to skip |
|
||||||
|
| `limit` | no | integer | Number of records to return (1-1000) |
|
||||||
|
| `search` | no | string | Substring match for delivery number, external number, or supplier name |
|
||||||
|
| `onlyUnbooked` | no | boolean | When true, returns only deliveries with unbooked stock items |
|
||||||
|
| `supplierId` | no | integer | Filter by supplier entity id |
|
||||||
|
| `seekMonths` | no | integer | Negative integer cutoff months ago (e.g. -1) |
|
||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|
||||||
Delivery list
|
Delivery list (windowed when pagination/filters are used)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -28,7 +39,11 @@ Delivery list
|
|||||||
"completelyOnLedgerPositions": 1
|
"completelyOnLedgerPositions": 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"total": 1,
|
||||||
|
"unfilteredTotal": 1,
|
||||||
|
"offset": 1,
|
||||||
|
"limit": 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
# `POST /delivery/parse`
|
|
||||||
|
|
||||||
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
|
||||||
|
|
||||||
Parse document (SSE)
|
|
||||||
|
|
||||||
Accepts an invoice or delivery note as PDF or text file
|
|
||||||
(`multipart/form-data`, field `file`) and streams extraction results as
|
|
||||||
Server-Sent Events until parsing is complete.
|
|
||||||
|
|
||||||
PDFs are read via `pdftotext -layout`; text files are decoded as UTF-8.
|
|
||||||
Pages are separated by the form feed character (`\f`). If no form feed
|
|
||||||
is present, the input is treated as a single page.
|
|
||||||
|
|
||||||
The extracted pages are fed to an LLM (OpenRouter) which fills a structured
|
|
||||||
manifest of header data and line items. The OpenRouter API key is read from
|
|
||||||
the authenticated user's company setting `keys.openrouter` (or the server's
|
|
||||||
`OPENROUTER_FREE_KEY` if not configured); the endpoint
|
|
||||||
returns `400` if the key is not configured.
|
|
||||||
|
|
||||||
The stream ends after the final `result` event (or an `error` event on failure).
|
|
||||||
|
|
||||||
|
|
||||||
## Body (`multipart/form-data`)
|
|
||||||
|
|
||||||
| Field | Required | Type | Description |
|
|
||||||
|-------|----------|------|-------------|
|
|
||||||
| `file` | yes | file | Document to parse (max 50 MB, application/pdf or text file) |
|
|
||||||
|
|
||||||
## SSE Events
|
|
||||||
|
|
||||||
The response is a `text/event-stream`. Events are emitted in order until the stream closes.
|
|
||||||
|
|
||||||
| Event | Description | Payload |
|
|
||||||
|-------|-------------|---------|
|
|
||||||
| `status` | Document parse confirmation and LLM status messages | `{ "message": "…" }` |
|
|
||||||
| `llm_call` | Fired before each LLM request | `{ "model": "…", "promptSize": 0, "messages": 0 }` |
|
|
||||||
| `tool_call` | Fired when the output_manifest tool is invoked | `{ "name": "output_manifest" }` |
|
|
||||||
| `usage` | Token usage after each LLM response | `{ "input": 0, "cached": 0, "output": 0, "cost": 0 }` |
|
|
||||||
| `progress` | Intermediate accumulated extraction state (basedata / notes / items) | `ManifestResult` |
|
|
||||||
| `result` | Final accumulated extraction — emitted just before the stream closes | `ManifestResult` |
|
|
||||||
| `error` | Emitted on document parse or LLM failure; stream closes immediately after | `{ "message": "…" }` |
|
|
||||||
|
|
||||||
## Response `200`
|
|
||||||
|
|
||||||
`text/event-stream` — emits `status`, `llm_call`, `tool_call`, `usage`,
|
|
||||||
`progress`, `result` (and `error` on failure). See SSE Events table below.
|
|
||||||
|
|
||||||
**ManifestResult shape** (used by `progress` and `result`):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"basedata": {
|
|
||||||
"date": "2026-01-15",
|
|
||||||
"lieferant_id": 42,
|
|
||||||
"lieferantName": "Muster GmbH",
|
|
||||||
"manifestNr": "LS-2026-001",
|
|
||||||
"manifestType": "lieferschein"
|
|
||||||
},
|
|
||||||
"notes": ["Pos. 3: VPE-Angabe fehlte, Standardwert 1 angenommen."],
|
|
||||||
"items": {
|
|
||||||
"pagesFrom": 1,
|
|
||||||
"pagesTo": 2,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"position": 1,
|
|
||||||
"count": 10,
|
|
||||||
"vpe": 1,
|
|
||||||
"unit": "Stk.",
|
|
||||||
"articleName": "Einhängesieb Stahl",
|
|
||||||
"liefArtNr": "ES-100",
|
|
||||||
"mwst": "19",
|
|
||||||
"eknetto": 450,
|
|
||||||
"page": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Errors
|
|
||||||
|
|
||||||
| Status | Description |
|
|
||||||
|--------|-------------|
|
|
||||||
| 400 | No file uploaded, unsupported file type, or OpenRouter key not configured — `{"error":"…"}` |
|
|
||||||
| 401 | Unauthorized — `{"error":"…"}` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
|
||||||
@@ -84,7 +84,7 @@ Updated delivery
|
|||||||
"size": 1,
|
"size": 1,
|
||||||
"created": "2026-08-10T00:00:00.000Z",
|
"created": "2026-08-10T00:00:00.000Z",
|
||||||
"modified": "2026-08-10T00:00:00.000Z",
|
"modified": "2026-08-10T00:00:00.000Z",
|
||||||
"thumbavif": "…",
|
"skeledash": "…",
|
||||||
"thumbx": 1,
|
"thumbx": 1,
|
||||||
"thumby": 1,
|
"thumby": 1,
|
||||||
"meta": {}
|
"meta": {}
|
||||||
|
|||||||
@@ -4,48 +4,83 @@ Protected. Requires a valid session cookie (`credentials: 'include'`).
|
|||||||
|
|
||||||
Subscribe to update signals (SSE)
|
Subscribe to update signals (SSE)
|
||||||
|
|
||||||
Opens a Server-Sent Events stream for the authenticated user's
|
Opens a Server-Sent Events stream for the authenticated user.
|
||||||
**selected company**. Clients receive named signals when another
|
Can be opened before a company is selected (`selectedCompanyId == null`);
|
||||||
request successfully mutates shared data for that company
|
when a company is selected or changed, the stream receives `user`
|
||||||
(including the same browser's other tabs).
|
(`{ selectedCompanyId }`) and `company` (`{ companyId }`) events and
|
||||||
|
updates its company scope in-place.
|
||||||
|
Clients also receive named signals when another request successfully
|
||||||
|
mutates shared data for the active company (including the same
|
||||||
|
browser's other tabs).
|
||||||
|
|
||||||
Use `EventSource` with credentials so the session cookie is sent:
|
Use `EventSource` with credentials so the session cookie is sent:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const es = new EventSource('https://…/api/events', { withCredentials: true });
|
const es = new EventSource('https://…/api/events', { withCredentials: true });
|
||||||
|
es.addEventListener('user', () => { refetchAuthMe(); });
|
||||||
|
es.addEventListener('company', () => { refetchCompany(); });
|
||||||
es.addEventListener('category', () => { refetchCategoryTree(); });
|
es.addEventListener('category', () => { refetchCategoryTree(); });
|
||||||
es.addEventListener('articles', () => { refetchArticleIndex(); });
|
es.addEventListener('articles', () => { refetchArticleIndex(); });
|
||||||
es.addEventListener('article-42000045', () => { refetchArticle('42000045'); });
|
es.addEventListener('article-42000045', () => { refetchArticle('42000045'); });
|
||||||
```
|
```
|
||||||
|
|
||||||
Keep-alive comments (`: heartbeat`) are sent roughly every 20s.
|
Keep-alive comments (`: heartbeat`) are sent roughly every 20s.
|
||||||
Reconnect after changing the selected company (the stream is bound
|
|
||||||
to the company at connect time).
|
|
||||||
|
|
||||||
## Signals
|
## Signals
|
||||||
|
|
||||||
| Signal | Emitted after successful |
|
Every SSE event uses the event name as the signal id; payloads are
|
||||||
|--------|--------------------------|
|
JSON and most events send an empty object `{}`. `user` emits
|
||||||
| `category` | `POST /category`, `PATCH /category/{id}`, `PUT /category/order`, `DELETE /category/{id}`, `POST /category/{id}/image`, `DELETE /category/{id}/image` |
|
`{ selectedCompanyId }`, `company` emits `{ companyId }`, and `brand`
|
||||||
| `articles` | `POST /article`, `PATCH /article/{sku}`, `DELETE /article/{sku}` |
|
emits `{ sender, brandId }`.
|
||||||
| `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}` |
|
|
||||||
|
| Signal | Payload | Emitted after successful |
|
||||||
|
|--------|---------|--------------------------|
|
||||||
|
| `user` | `{ selectedCompanyId }` | `PUT /auth/selected-company`, `POST /company`, `DELETE /company/{id}` |
|
||||||
|
| `company` | `{ companyId }` | `PUT /auth/selected-company`, `POST /company`, `PUT /company/{id}`, `DELETE /company/{id}` |
|
||||||
|
| `category` | `{}` | `POST /category`, `PATCH /category/{id}`, `PUT /category/order`, `DELETE /category/{id}`, `POST /category/{id}/image`, `DELETE /category/{id}/image`, `PUT /category/{id}/property` |
|
||||||
|
| `articles` | `{}` | `POST /article`, `PATCH /article/{sku}`, `DELETE /article/{sku}`, `PUT /article/{sku}/articles`-style list-level changes, `POST /category/{id}/article` and related article-assigning category mutations |
|
||||||
|
| `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}`, supplier/article-property mutations affecting the article |
|
||||||
|
| `stock` | `{}` | `POST /stock/adjust`, `POST /company/import` (also `category`, `articles`, `brand`, `attributename`, `warehouse`, `carrier`, `supplier`, `order`, `shipment`, `ledger`) |
|
||||||
|
| `ledger` | `{}` | `POST /stock/adjust` |
|
||||||
|
| `attributename` | `{}` | `POST /attributename`, `PUT /attributename/order`, `PATCH /attributename/{id}`, `DELETE /attributename/{id}` |
|
||||||
|
| `propertyname` | `{}` | `POST /propertyname`, `PUT /propertyname/order`, `PATCH /propertyname/{id}`, `DELETE /propertyname/{id}` |
|
||||||
|
| `labeltemplate` | `{}` | `POST /labeltemplate`, `PUT /labeltemplate/{id}`, `PATCH /labeltemplate/{id}`, `DELETE /labeltemplate/{id}` |
|
||||||
|
| `printer` | `{}` | `POST /printer`, `PUT /printer/{id}`, `PATCH /printer/{id}`, `DELETE /printer/{id}` |
|
||||||
|
| `printeragent` | `{}` | `PUT /printeragent`, `PATCH /printeragent/{id}`, `DELETE /printeragent/{id}`, printer agent connect/disconnect/state |
|
||||||
|
| `agentprinter` | `{ printers }` or `{}` | `PATCH /agentprinter/{id}`, `PUT /agentprinter/order`, `DELETE /agentprinter/{id}`, agent state updates / printer discovery |
|
||||||
|
| `companyuser` | `{}` | `POST /companyuser`, `PATCH /companyuser/{id}`, `DELETE /companyuser/{id}` |
|
||||||
|
| `autoinvite` | `{}` | `POST /autoinvite`, `DELETE /autoinvite/{id}` |
|
||||||
|
| `warehouse` | `{}` | `POST /warehouse`, `PUT /warehouse/{id}`, `PATCH /warehouse/{id}`, `DELETE /warehouse/{id}` |
|
||||||
|
| `supplier` | `{}` | `POST /supplier`, `PATCH /supplier/{id}`, `DELETE /supplier/{id}` |
|
||||||
|
| `vfs` | `{}` | VFS uploads and filesystem mutations such as `POST /vfs/upload`, `POST /vfs/mkdir`, `POST /vfs/rename`, `PUT /vfs/{*path}`, `DELETE /vfs/{*path}` |
|
||||||
|
| `modelconversation` | `{}` | `POST /modelconversation`, `DELETE /modelconversation/{id}` |
|
||||||
|
| `modelconversation-<id>` | `{}` | Conversation-specific updates on the same model conversation thread; the concrete ID is used in the event name |
|
||||||
|
| `brand` | `{ sender, brandId }` | `POST /brand`, `PATCH /brand/{id}`, `DELETE /brand/{id}`, `POST /brand/{id}/image`, `DELETE /brand/{id}/image` |
|
||||||
|
|
||||||
|
The `brand` payload is `{ sender, brandId }`: `sender` echoes the
|
||||||
|
caller's `x-client-id` header (empty string if absent) so the sender
|
||||||
|
can ignore its own event; `brandId` is the affected brand id (`null`
|
||||||
|
only if unknown). All other signals send `{}`.
|
||||||
|
|
||||||
Signals are only sent when the mutation actually succeeds (2xx).
|
Signals are only sent when the mutation actually succeeds (2xx).
|
||||||
Failed or no-op validation errors do not emit. The `article-<sku>`
|
Failed or no-op validation errors do not emit. The `article-<sku>`
|
||||||
event name uses the concrete SKU (e.g. `article-42000045`).
|
and `modelconversation-<id>` event names use the concrete item id at
|
||||||
|
runtime (for example `article-42000045` and `modelconversation-123`).
|
||||||
|
|
||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|
||||||
SSE stream (`text/event-stream`). Each signal is an SSE event
|
SSE stream (`text/event-stream`). Each signal is an SSE event
|
||||||
whose name is the signal id and whose data is `{}`.
|
whose name is the signal id. Most signals send `{}` as data;
|
||||||
|
`user` sends `{ "selectedCompanyId": 1 }`, `company` sends
|
||||||
|
`{ "companyId": 1 }`, and `brand` sends
|
||||||
|
`{ "sender": "<x-client-id>", "brandId": 12 }`.
|
||||||
|
|
||||||
|
|
||||||
## Errors
|
## Errors
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| 400 | No company selected — `{"error":"…"}` |
|
|
||||||
| 401 | Unauthorized — `{"error":"…"}` |
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user