Files
dashback/src/api/routes/company.js
2026-09-30 22:37:34 +02:00

307 lines
10 KiB
JavaScript

import * as db from '../../db/company.js';
import * as googleAuth from '../../db/googleAuth.js';
import { clearCachedUser } from '../middleware/auth.js';
function userPayload(authRow) {
return {
id: authRow.id,
email: authRow.email,
selectedCompanyId: authRow.selectedCompanyId,
};
}
function parseCompanyBody(body) {
const { iso639, name, taxrate, reducedtaxrate } = body ?? {};
if (!iso639 || !name || taxrate == null || reducedtaxrate == null) {
return { error: 'iso639, name, taxrate, and reducedtaxrate are required' };
}
if (typeof taxrate !== 'number' || !Number.isFinite(taxrate) || taxrate < 0) {
return { error: 'taxrate must be a non-negative number' };
}
if (typeof reducedtaxrate !== 'number' || !Number.isFinite(reducedtaxrate) || reducedtaxrate < 0) {
return { error: 'reducedtaxrate must be a non-negative number' };
}
return { iso639, name, taxrate, reducedtaxrate };
}
function parseCompanyId(id) {
const companyId = Number(id);
if (!Number.isInteger(companyId) || companyId <= 0) {
return { error: 'Invalid company id' };
}
return { companyId };
}
function companyErrorStatus(error) {
if (error.message === 'Company not found') return 404;
if (error.message === 'Company name not available') return 409;
if (error.message === 'Invalid tax rate') return 400;
return 500;
}
/**
* Registers company CRUD routes (requires auth middleware).
* @param {import('express').Application} app - Express app
* @param {string} [basePath=''] - Optional URL prefix for all routes in this module
*/
export function register(app, basePath = '') {
/**
* @openapi
* /company:
* get:
* tags: [Company]
* summary: List companies
* description: Lists companies for the authenticated user.
* security:
* - cookieAuth: []
* x-markdown: company/list.md
* responses:
* 200:
* description: Company list
* content:
* application/json:
* schema:
* type: object
* properties:
* companies:
* type: array
* items:
* $ref: '#/components/schemas/Company'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 500:
* description: Server error
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* post:
* tags: [Company]
* summary: Create company
* description: Creates a company and selects it for the current user.
* security:
* - cookieAuth: []
* x-markdown: company/create.md
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [iso639, name, taxrate, reducedtaxrate]
* properties:
* iso639:
* type: string
* name:
* type: string
* taxrate:
* type: number
* reducedtaxrate:
* type: number
* example:
* iso639: de
* name: Acme GmbH
* taxrate: 19
* reducedtaxrate: 7
* responses:
* 201:
* description: Created; selected company updated
* content:
* application/json:
* schema:
* type: object
* properties:
* company:
* $ref: '#/components/schemas/Company'
* user:
* $ref: '#/components/schemas/User'
* 400:
* description: Validation error
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 409:
* description: Company name not available
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.get(`${basePath}/company`, async (req, res) => {
try {
const companies = await db.listCompanies(req.user.id);
res.status(200).json({ companies });
} catch (error) {
console.error('List companies failed:', error);
res.status(500).json({ error: 'Failed to list companies' });
}
});
app.post(`${basePath}/company`, async (req, res) => {
const body = parseCompanyBody(req.body);
if (body.error) {
return res.status(400).json({ error: body.error });
}
try {
const company = await db.createCompany(req.user.id, body);
await googleAuth.setSelectedCompany(req.user.id, company.id);
clearCachedUser(req.user.id);
const authRow = await googleAuth.getAllowedEmail(req.user.email);
res.status(201).json({ company, user: userPayload(authRow) });
} catch (error) {
const status = companyErrorStatus(error);
if (status === 500) console.error('Create company failed:', error);
res.status(status).json({ error: error.message });
}
});
/**
* @openapi
* /company/{id}:
* put:
* tags: [Company]
* summary: Update company
* description: Updates a company.
* security:
* - cookieAuth: []
* x-markdown: company/update.md
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [iso639, name, taxrate, reducedtaxrate]
* properties:
* iso639:
* type: string
* name:
* type: string
* taxrate:
* type: number
* reducedtaxrate:
* type: number
* responses:
* 200:
* description: Updated
* content:
* application/json:
* schema:
* type: object
* properties:
* company:
* $ref: '#/components/schemas/Company'
* 400:
* description: Validation error
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Company not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 409:
* description: Company name not available
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* delete:
* tags: [Company]
* summary: Delete company
* description: Deletes a company.
* security:
* - cookieAuth: []
* x-markdown: company/delete.md
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* responses:
* 204:
* description: Deleted
* 400:
* description: Invalid id
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 500:
* description: Server error
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.put(`${basePath}/company/:id`, async (req, res) => {
const parsedId = parseCompanyId(req.params.id);
if (parsedId.error) {
return res.status(400).json({ error: parsedId.error });
}
const body = parseCompanyBody(req.body);
if (body.error) {
return res.status(400).json({ error: body.error });
}
try {
const company = await db.updateCompany(parsedId.companyId, body);
res.status(200).json({ company });
} catch (error) {
const status = companyErrorStatus(error);
if (status === 500) console.error('Update company failed:', error);
res.status(status).json({ error: error.message });
}
});
app.delete(`${basePath}/company/:id`, async (req, res) => {
const parsedId = parseCompanyId(req.params.id);
if (parsedId.error) {
return res.status(400).json({ error: parsedId.error });
}
try {
await db.deleteCompany(parsedId.companyId);
res.sendStatus(204);
} catch (error) {
console.error('Delete company failed:', error);
res.status(500).json({ error: 'Failed to delete company' });
}
});
}