Files
dashback/src/api/routes/customergroup.js
seb e6c24654dd Align store calls with selected-company scoping and add entity numbers.
Drop companyId from company-scoped store wrappers and routes, add GET /entity/next-number plus optional number on entity create/update, and document dashdbschema API.md lookup in sibling-repos.
2026-08-07 06:22:13 +02:00

430 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as db from '../../db/customergroup.js';
/** Implicit groups created by the system — name locked. */
const LOCKED_NAMES = new Set(['POS', 'Lieferanten']);
function requireSelectedCompany(req) {
const companyId = req.user.selectedCompanyId;
if (companyId == null) {
return { error: 'No company selected' };
}
return { companyId };
}
function customergroupErrorStatus(error) {
if (error.message === 'No company selected') return 400;
if (error.message === 'Not authorized') return 403;
if (error.message === 'Company not found') return 404;
if (error.message === 'Customer group not found') return 404;
if (error.message === 'Name required') return 400;
if (error.message === 'Name not allowed') return 400;
if (error.message === 'Name cannot be changed') return 400;
if (error.message === 'Invalid customer group order') return 400;
if (error.message === 'Customer group in use') return 409;
return 500;
}
function withEditable(row) {
return {
...row,
editable: !LOCKED_NAMES.has(row.name),
};
}
/**
* Registers customergroup routes for the authenticated user's selected company.
* @param {import('express').Application} app
* @param {string} [basePath='']
*/
export function register(app, basePath = '') {
/**
* @openapi
* /customergroup:
* get:
* tags: [Customergroup]
* summary: List customer groups
* description: |
* Lists customer groups for the selected company, ordered by `sort`.
* Each item includes `editable`: `false` for implicit `POS` / `Lieferanten`
* (name locked), `true` otherwise. Reorder via `PUT /customergroup/order`.
* security:
* - cookieAuth: []
* x-markdown: customergroup/list.md
* responses:
* 200:
* description: Customer group list
* content:
* application/json:
* schema:
* type: object
* properties:
* customergroups:
* type: array
* items:
* $ref: '#/components/schemas/CustomergroupListItem'
* 400:
* description: No company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 403:
* description: Not authorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Company not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* post:
* tags: [Customergroup]
* summary: Create customer group
* description: |
* Creates a customer group for the selected company.
* Appends with the next `sort` index. Reserved names `POS` and `Lieferanten`
* are not allowed (`Name not allowed`).
* security:
* - cookieAuth: []
* x-markdown: customergroup/create.md
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [name]
* properties:
* name:
* type: string
* example:
* name: Kunden
* responses:
* 201:
* description: Created customer group
* content:
* application/json:
* schema:
* type: object
* properties:
* customergroup:
* $ref: '#/components/schemas/Customergroup'
* 400:
* description: Name required, name not allowed, or no company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 403:
* description: Not authorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Company not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.get(`${basePath}/customergroup`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
try {
const rows = await db.list(req.user.id);
res.status(200).json({ customergroups: rows.map(withEditable) });
} catch (error) {
const status = customergroupErrorStatus(error);
if (status === 500) console.error('List customergroups failed:', error);
res.status(status).json({ error: error.message });
}
});
app.post(`${basePath}/customergroup`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
const { name } = req.body ?? {};
try {
const customergroup = await db.create(req.user.id, { name });
res.status(201).json({ customergroup });
} catch (error) {
const status = customergroupErrorStatus(error);
if (status === 500) console.error('Create customergroup failed:', error);
res.status(status).json({ error: error.message });
}
});
/**
* @openapi
* /customergroup/order:
* put:
* tags: [Customergroup]
* summary: Reorder customer groups
* description: |
* Sets `sort` to each id’s index `0…n-1` in one transaction.
* `customergroupIds` must be exactly the company’s groups (same set, no dups)
* else `Invalid customer group order`.
* security:
* - cookieAuth: []
* x-markdown: customergroup/order.md
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [customergroupIds]
* properties:
* customergroupIds:
* type: array
* items:
* type: integer
* example:
* customergroupIds: [4, 3, 1, 2]
* responses:
* 200:
* description: New order
* content:
* application/json:
* schema:
* type: object
* properties:
* customergroupIds:
* type: array
* items:
* type: integer
* 400:
* description: Invalid customer group order or no company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 403:
* description: Not authorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Company not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.put(`${basePath}/customergroup/order`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
const rawIds = req.body?.customergroupIds;
if (!Array.isArray(rawIds)) {
return res.status(400).json({ error: 'customergroupIds is required' });
}
const customergroupIds = [];
for (const value of rawIds) {
const id = Number(value);
if (!Number.isInteger(id) || id <= 0) {
return res.status(400).json({ error: 'customergroupIds must be positive integers' });
}
customergroupIds.push(id);
}
try {
const result = await db.order(req.user.id, { customergroupIds });
res.status(200).json(result);
} catch (error) {
const status = customergroupErrorStatus(error);
if (status === 500) console.error('Order customergroups failed:', error);
res.status(status).json({ error: error.message });
}
});
/**
* @openapi
* /customergroup/{id}:
* patch:
* tags: [Customergroup]
* summary: Update customer group
* description: |
* Renames a customer group. Implicit `POS` / `Lieferanten` cannot be
* renamed (`Name cannot be changed`). Reorder via `PUT /customergroup/order`.
* security:
* - cookieAuth: []
* x-markdown: customergroup/update.md
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [name]
* properties:
* name:
* type: string
* example:
* name: Stammkunden
* responses:
* 200:
* description: Updated customer group
* content:
* application/json:
* schema:
* type: object
* properties:
* customergroup:
* $ref: '#/components/schemas/Customergroup'
* 400:
* description: Name required, name cannot be changed, invalid id, or no company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 403:
* description: Not authorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Customer group or company not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* delete:
* tags: [Customergroup]
* summary: Delete customer group
* description: |
* Deletes the customer group (cascades entities). Fails with
* `Customer group in use` for the POS group or other NO ACTION references.
* security:
* - cookieAuth: []
* x-markdown: customergroup/delete.md
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* responses:
* 204:
* description: Deleted
* 400:
* description: No company selected or invalid id
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 403:
* description: Not authorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Customer group not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 409:
* description: Customer group in use
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.patch(`${basePath}/customergroup/:id`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
const id = Number(req.params.id);
if (!Number.isInteger(id) || id <= 0) {
return res.status(400).json({ error: 'id is required' });
}
const { name } = req.body ?? {};
try {
const customergroup = await db.update(req.user.id, id, { name });
res.status(200).json({ customergroup });
} catch (error) {
const status = customergroupErrorStatus(error);
if (status === 500) console.error('Update customergroup failed:', error);
res.status(status).json({ error: error.message });
}
});
app.delete(`${basePath}/customergroup/:id`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
const id = Number(req.params.id);
if (!Number.isInteger(id) || id <= 0) {
return res.status(400).json({ error: 'id is required' });
}
try {
await db.remove(req.user.id, id);
res.sendStatus(204);
} catch (error) {
const status = customergroupErrorStatus(error);
if (status === 500) console.error('Delete customergroup failed:', error);
res.status(status).json({ error: error.message });
}
});
}