import * as db from '../../db/customergroup.js'; // #region Helpers /** 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), }; } // #endregion /** * 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' */ // #region GET /customergroup 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 }); } }); // #endregion // #region POST /customergroup 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 }); } }); // #endregion /** * @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' */ // #region PUT /customergroup/order 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 }); } }); // #endregion /** * @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' */ // #region PATCH /customergroup/:id 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 }); } }); // #endregion // #region DELETE /customergroup/:id 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 }); } }); // #endregion }