u
This commit is contained in:
@@ -217,6 +217,20 @@
|
||||
* format: date-time
|
||||
* text:
|
||||
* type: string
|
||||
* CompanyHistoryEntry:
|
||||
* type: object
|
||||
* required: [id, date, text]
|
||||
* properties:
|
||||
* id:
|
||||
* type: integer
|
||||
* companyuserId:
|
||||
* type: integer
|
||||
* nullable: true
|
||||
* date:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* text:
|
||||
* type: string
|
||||
* ArticleSupplierPrice:
|
||||
* type: object
|
||||
* required: [net, taxId]
|
||||
|
||||
@@ -84,26 +84,11 @@ export function register(app, basePath = '') {
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: [attributevalues]
|
||||
* properties:
|
||||
* attributevalues:
|
||||
* type: array
|
||||
* description: Array of attribute value IDs (numbers) or objects with id or attributeValueId
|
||||
* items:
|
||||
* oneOf:
|
||||
* - type: integer
|
||||
* description: Attribute value ID
|
||||
* - type: object
|
||||
* properties:
|
||||
* id:
|
||||
* type: integer
|
||||
* description: Attribute value ID
|
||||
* attributeValueId:
|
||||
* type: integer
|
||||
* description: Attribute value ID
|
||||
* sort:
|
||||
* type: integer
|
||||
* type: array
|
||||
* description: Array of attribute value IDs. Array order defines sort.
|
||||
* items:
|
||||
* type: integer
|
||||
* example: [166]
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Updated article attribute values
|
||||
@@ -147,13 +132,10 @@ export function register(app, basePath = '') {
|
||||
return res.status(400).json({ error: skuParsed.error });
|
||||
}
|
||||
|
||||
const valuesInput = req.body?.attributevalues ?? req.body?.attributeValues ?? req.body?.attribute_values ?? req.body?.values ?? req.body;
|
||||
if (!Array.isArray(valuesInput)) {
|
||||
return res.status(400).json({ error: 'attributevalues array required' });
|
||||
}
|
||||
if (!Array.isArray(req.body)) return res.status(400).json({ error: 'attributevalues array required' });
|
||||
|
||||
try {
|
||||
const attributevalues = await db.setByArticleSku(req.user.id, skuParsed.sku, valuesInput);
|
||||
const attributevalues = await db.setByArticleSku(req.user.id, skuParsed.sku, req.body);
|
||||
emitArticleSignal(req, skuParsed.sku);
|
||||
res.json({ attributevalues });
|
||||
} catch (error) {
|
||||
|
||||
@@ -17,6 +17,7 @@ function attributeErrorStatus(error) {
|
||||
if (error.message === 'Name required') return 400;
|
||||
if (error.message === 'Invalid attribute name order') return 400;
|
||||
if (error.message === 'Invalid id') return 400;
|
||||
if (error.message === 'Attribute name in use' || error.message?.includes('FOREIGN KEY constraint')) return 409;
|
||||
return 500;
|
||||
}
|
||||
|
||||
@@ -200,6 +201,42 @@ export function register(app, basePath = '') {
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Deleted successfully
|
||||
* 400:
|
||||
* description: Bad request 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: Forbidden
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 404:
|
||||
* description: Attribute name not found
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 409:
|
||||
* description: Attribute name in use
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 500:
|
||||
* description: Server error
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
*/
|
||||
app.patch(`${basePath}/attributename/:id`, async (req, res) => {
|
||||
const selected = requireSelectedCompany(req);
|
||||
@@ -213,7 +250,9 @@ export function register(app, basePath = '') {
|
||||
emit('attributename', { companyId: selected.companyId });
|
||||
res.json({ attributename });
|
||||
} catch (error) {
|
||||
res.status(attributeErrorStatus(error)).json({ error: error.message });
|
||||
const status = attributeErrorStatus(error);
|
||||
if (status === 500) console.error('Update attribute name failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -229,7 +268,12 @@ export function register(app, basePath = '') {
|
||||
emit('attributename', { companyId: selected.companyId });
|
||||
res.json({ ok: true });
|
||||
} catch (error) {
|
||||
res.status(attributeErrorStatus(error)).json({ error: error.message });
|
||||
const status = attributeErrorStatus(error);
|
||||
const message = (error.message === 'Attribute name in use' || error.message?.includes('FOREIGN KEY constraint'))
|
||||
? 'Attribute name in use'
|
||||
: error.message;
|
||||
if (status === 500) console.error('Delete attribute name failed:', error);
|
||||
res.status(status).json({ error: message });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ function attributeValueErrorStatus(error) {
|
||||
if (error.message === 'Value required') return 400;
|
||||
if (error.message === 'Invalid attribute value order') return 400;
|
||||
if (error.message === 'Invalid id' || error.message === 'Invalid attributeId') return 400;
|
||||
if (error.message === 'Attribute value in use' || error.message?.includes('FOREIGN KEY constraint')) return 409;
|
||||
return 500;
|
||||
}
|
||||
|
||||
@@ -216,6 +217,42 @@ export function register(app, basePath = '') {
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Deleted successfully
|
||||
* 400:
|
||||
* description: Bad request 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: Forbidden
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 404:
|
||||
* description: Attribute value not found
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 409:
|
||||
* description: Attribute value in use
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 500:
|
||||
* description: Server error
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
*/
|
||||
app.patch(`${basePath}/attributevalue/:id`, async (req, res) => {
|
||||
const selected = requireSelectedCompany(req);
|
||||
@@ -229,7 +266,9 @@ export function register(app, basePath = '') {
|
||||
emit('attributename', { companyId: selected.companyId });
|
||||
res.json({ attributevalue });
|
||||
} catch (error) {
|
||||
res.status(attributeValueErrorStatus(error)).json({ error: error.message });
|
||||
const status = attributeValueErrorStatus(error);
|
||||
if (status === 500) console.error('Update attribute value failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -245,7 +284,12 @@ export function register(app, basePath = '') {
|
||||
emit('attributename', { companyId: selected.companyId });
|
||||
res.json({ ok: true });
|
||||
} catch (error) {
|
||||
res.status(attributeValueErrorStatus(error)).json({ error: error.message });
|
||||
const status = attributeValueErrorStatus(error);
|
||||
const message = (error.message === 'Attribute value in use' || error.message?.includes('FOREIGN KEY constraint'))
|
||||
? 'Attribute value in use'
|
||||
: error.message;
|
||||
if (status === 500) console.error('Delete attribute value failed:', error);
|
||||
res.status(status).json({ error: message });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -400,6 +400,7 @@ export function register(app, basePath = '') {
|
||||
.map((a) => ({
|
||||
...a,
|
||||
childCount: Number(a.childCount ?? 0),
|
||||
isFather: Boolean(a.isFather ?? a.is_father),
|
||||
}));
|
||||
res.status(200).json({ articles });
|
||||
} catch (error) {
|
||||
|
||||
@@ -85,26 +85,11 @@ export function register(app, basePath = '') {
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: [attributevalues]
|
||||
* properties:
|
||||
* attributevalues:
|
||||
* type: array
|
||||
* description: Array of attribute value IDs (numbers) or objects with id or attributeValueId
|
||||
* items:
|
||||
* oneOf:
|
||||
* - type: integer
|
||||
* description: Attribute value ID
|
||||
* - type: object
|
||||
* properties:
|
||||
* id:
|
||||
* type: integer
|
||||
* description: Attribute value ID
|
||||
* attributeValueId:
|
||||
* type: integer
|
||||
* description: Attribute value ID
|
||||
* sort:
|
||||
* type: integer
|
||||
* type: array
|
||||
* description: Array of attribute value IDs. Array order defines sort.
|
||||
* items:
|
||||
* type: integer
|
||||
* example: [166]
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Updated category attribute values
|
||||
@@ -148,13 +133,10 @@ export function register(app, basePath = '') {
|
||||
return res.status(400).json({ error: idParsed.error });
|
||||
}
|
||||
|
||||
const valuesInput = req.body?.attributevalues ?? req.body?.attributeValues ?? req.body?.attribute_values ?? req.body?.values ?? req.body;
|
||||
if (!Array.isArray(valuesInput)) {
|
||||
return res.status(400).json({ error: 'attributevalues array required' });
|
||||
}
|
||||
if (!Array.isArray(req.body)) return res.status(400).json({ error: 'attributevalues array required' });
|
||||
|
||||
try {
|
||||
const attributevalues = await db.setByCategoryId(req.user.id, idParsed.id, valuesInput);
|
||||
const attributevalues = await db.setByCategoryId(req.user.id, idParsed.id, req.body);
|
||||
emitCategorySignal(req);
|
||||
res.json({ attributevalues });
|
||||
} catch (error) {
|
||||
|
||||
@@ -32,10 +32,21 @@ function parseCompanyId(id) {
|
||||
return { companyId };
|
||||
}
|
||||
|
||||
function requireSelectedCompany(req) {
|
||||
const id = req.user?.selectedCompanyId;
|
||||
if (id == null) {
|
||||
const err = new Error('No company selected');
|
||||
err.statusCode = 400;
|
||||
throw err;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
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;
|
||||
if (error.message === 'Invalid tax rate' || error.message === 'Text required' || error.message === 'No company selected') return 400;
|
||||
if (error.message === 'Not authorized') return 403;
|
||||
return 500;
|
||||
}
|
||||
|
||||
@@ -167,6 +178,170 @@ export function register(app, basePath = '') {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /company/history:
|
||||
* get:
|
||||
* tags: [Company]
|
||||
* summary: Get company history
|
||||
* description: |
|
||||
* Returns `companyhistory` rows for the selected company, newest first (`date` DESC, `id` DESC).
|
||||
* security:
|
||||
* - cookieAuth: []
|
||||
* x-markdown: company/history.md
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: limit
|
||||
* required: false
|
||||
* schema:
|
||||
* type: integer
|
||||
* minimum: 1
|
||||
* maximum: 1000
|
||||
* description: Maximum number of entries to return (newest first). Omit for all entries.
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Company history (newest first)
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: [history, truncated]
|
||||
* properties:
|
||||
* history:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/CompanyHistoryEntry'
|
||||
* truncated:
|
||||
* type: boolean
|
||||
* description: True when a limit was applied and the result set was exactly that size (more entries may exist).
|
||||
* 400:
|
||||
* description: Invalid limit 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: Forbidden
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 500:
|
||||
* description: Server error
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* post:
|
||||
* tags: [Company]
|
||||
* summary: Add company history entry
|
||||
* description: |
|
||||
* Appends an entry to `companyhistory` for the selected company.
|
||||
* security:
|
||||
* - cookieAuth: []
|
||||
* x-markdown: company/addHistory.md
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: [text]
|
||||
* properties:
|
||||
* text:
|
||||
* type: string
|
||||
* description: History entry text or note
|
||||
* responses:
|
||||
* 201:
|
||||
* description: History entry created
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: [history]
|
||||
* properties:
|
||||
* history:
|
||||
* $ref: '#/components/schemas/CompanyHistoryEntry'
|
||||
* 400:
|
||||
* description: Text required 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: Forbidden
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 500:
|
||||
* description: Server error
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
*/
|
||||
app.get(`${basePath}/company/history`, async (req, res) => {
|
||||
try {
|
||||
requireSelectedCompany(req);
|
||||
} catch (error) {
|
||||
return res.status(error.statusCode || 400).json({ error: error.message });
|
||||
}
|
||||
|
||||
const rawLimit = req.query.limit;
|
||||
let limit = null;
|
||||
if (rawLimit !== undefined) {
|
||||
limit = parseInt(rawLimit, 10);
|
||||
if (!Number.isInteger(limit) || limit < 1 || limit > 1000) {
|
||||
return res.status(400).json({ error: 'limit must be an integer between 1 and 1000' });
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const history = await db.getCompanyHistory(req.user.id, limit != null ? { limit } : undefined);
|
||||
const truncated = limit != null && history.length === limit;
|
||||
res.status(200).json({ history, truncated });
|
||||
} catch (error) {
|
||||
const status = companyErrorStatus(error);
|
||||
if (status === 500) console.error('Get company history failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.post(`${basePath}/company/history`, async (req, res) => {
|
||||
try {
|
||||
requireSelectedCompany(req);
|
||||
} catch (error) {
|
||||
return res.status(error.statusCode || 400).json({ error: error.message });
|
||||
}
|
||||
|
||||
const text = typeof req.body?.text === 'string' ? req.body.text.trim() : '';
|
||||
if (!text) {
|
||||
return res.status(400).json({ error: 'text is required' });
|
||||
}
|
||||
|
||||
try {
|
||||
const entry = await db.addCompanyHistory(req.user.id, { text });
|
||||
res.status(201).json({ history: entry });
|
||||
} catch (error) {
|
||||
const status = companyErrorStatus(error);
|
||||
if (status === 500) console.error('Add company history failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /company/{id}:
|
||||
|
||||
@@ -17,6 +17,7 @@ function propertyErrorStatus(error) {
|
||||
if (error.message === 'Name required') return 400;
|
||||
if (error.message === 'Invalid property name order') return 400;
|
||||
if (error.message === 'Invalid id') return 400;
|
||||
if (error.message === 'Property name in use' || error.message?.includes('FOREIGN KEY constraint')) return 409;
|
||||
return 500;
|
||||
}
|
||||
|
||||
@@ -214,6 +215,42 @@ export function register(app, basePath = '') {
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Deleted successfully
|
||||
* 400:
|
||||
* description: Bad request 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: Forbidden
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 404:
|
||||
* description: Property name not found
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 409:
|
||||
* description: Property name in use
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 500:
|
||||
* description: Server error
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
*/
|
||||
app.patch(`${basePath}/propertyname/:id`, async (req, res) => {
|
||||
const selected = requireSelectedCompany(req);
|
||||
@@ -227,7 +264,9 @@ export function register(app, basePath = '') {
|
||||
emit('propertyname', { companyId: selected.companyId });
|
||||
res.json({ propertyname });
|
||||
} catch (error) {
|
||||
res.status(propertyErrorStatus(error)).json({ error: error.message });
|
||||
const status = propertyErrorStatus(error);
|
||||
if (status === 500) console.error('Update property name failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -243,7 +282,12 @@ export function register(app, basePath = '') {
|
||||
emit('propertyname', { companyId: selected.companyId });
|
||||
res.json({ ok: true });
|
||||
} catch (error) {
|
||||
res.status(propertyErrorStatus(error)).json({ error: error.message });
|
||||
const status = propertyErrorStatus(error);
|
||||
const message = (error.message === 'Property name in use' || error.message?.includes('FOREIGN KEY constraint'))
|
||||
? 'Property name in use'
|
||||
: error.message;
|
||||
if (status === 500) console.error('Delete property name failed:', error);
|
||||
res.status(status).json({ error: message });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user