u
This commit is contained in:
413
src/api/article.js
Normal file
413
src/api/article.js
Normal file
@@ -0,0 +1,413 @@
|
||||
import { http, httpBinary, httpGetBinary } from './http.js';
|
||||
|
||||
/**
|
||||
* NOTE: there is no `articleIndex` namespace anymore.
|
||||
*
|
||||
* The whole-company search index (`GET /api/articleIndex`) was abandoned. Article
|
||||
* lists are fetched per category via `category.getArticles(categoryId)`
|
||||
* (`GET /api/category/:id/articles`); `categoryAutomat.loadTree()` warms every
|
||||
* category view right after the tree loads, and `articleIndexAutomat` derives the
|
||||
* client-side search index from those views.
|
||||
*/
|
||||
|
||||
export const article = {
|
||||
search({ target, filter, offset, maxCount } = {}) {
|
||||
const params = new URLSearchParams({ target });
|
||||
if (filter) params.set('filter', filter);
|
||||
if (offset) params.set('offset', String(offset));
|
||||
if (maxCount != null) params.set('maxCount', String(maxCount));
|
||||
return http(`/api/article/search?${params}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* GET /api/article/:sku → { article }
|
||||
* Detail: prices[], photos[], eans[], brand, unit, categoryIds, …
|
||||
* (Flat net/taxRate/fullHash/thumb* only on category tree / getCategoryArticles.)
|
||||
*/
|
||||
getBySku(sku) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* GET /api/article/history
|
||||
* History of all articles with paging/sorting/filtering
|
||||
*/
|
||||
getHistory({ startRow, endRow, offset, limit, sortModel, filterModel } = {}) {
|
||||
const params = new URLSearchParams();
|
||||
if (startRow != null) params.set('startRow', String(startRow));
|
||||
if (endRow != null) params.set('endRow', String(endRow));
|
||||
if (offset != null) params.set('offset', String(offset));
|
||||
if (limit != null) params.set('limit', String(limit));
|
||||
if (sortModel && sortModel.length) params.set('sort', JSON.stringify(sortModel));
|
||||
if (filterModel && Object.keys(filterModel).length) params.set('filter', JSON.stringify(filterModel));
|
||||
const qs = params.toString();
|
||||
return http(`/api/article/history${qs ? `?${qs}` : ''}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* GET /api/article/:sku/history
|
||||
*/
|
||||
getBySkuHistory(sku, { limit } = {}) {
|
||||
const params = new URLSearchParams();
|
||||
if (limit != null) params.set('limit', String(limit));
|
||||
const qs = params.toString();
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/history${qs ? `?${qs}` : ''}`);
|
||||
},
|
||||
|
||||
/** POST /api/article → 201 { article } */
|
||||
create({ categoryId, name, track_stock }) {
|
||||
return http('/api/article', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
categoryId: Number(categoryId),
|
||||
name,
|
||||
...(track_stock !== undefined ? { track_stock: Boolean(track_stock) } : {}),
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* PATCH /api/article/:sku
|
||||
* Body: at least one of { name, unitamount, unitSi, track_stock, desc, seo }
|
||||
* unitSi must exist for selected company → 404 Unit not found
|
||||
* → 200 { article } (same detail shape as GET: includes desc, seo)
|
||||
*/
|
||||
update(sku, body) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}`, {
|
||||
method: 'PATCH',
|
||||
body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* PUT /api/article/:sku/track_stock
|
||||
* Body: { track_stock: boolean }
|
||||
* → 200 { article }
|
||||
*/
|
||||
setTrackStock(sku, body) {
|
||||
const payload = typeof body === 'boolean' ? { track_stock: body } : body;
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/track_stock`, {
|
||||
method: 'PUT',
|
||||
body: payload,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* DELETE /api/article/:sku → 204
|
||||
* Errors: 400 Article has children, 401, 403, 404 Article not found
|
||||
*/
|
||||
delete(sku, { cascade = false } = {}) {
|
||||
const qs = cascade ? '?cascade=true' : '';
|
||||
return http(`/api/article/${encodeURIComponent(sku)}${qs}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* POST /api/article/:sku/image
|
||||
* Body: raw image bytes (Content-Type: image/webp, max 1000×1000)
|
||||
* → 201 { image: { picId, sort, full, thumb, fullHash, thumbHash } }
|
||||
*/
|
||||
uploadImage(sku, file) {
|
||||
return httpBinary(`/api/article/${encodeURIComponent(sku)}/image`, {
|
||||
method: 'POST',
|
||||
contentType: file?.type || 'image/webp',
|
||||
body: file,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* DELETE /api/article/:sku/image/:fullHash → 204
|
||||
*/
|
||||
deleteImage(sku, fullHash) {
|
||||
return http(
|
||||
`/api/article/${encodeURIComponent(sku)}/image/${encodeURIComponent(fullHash)}`,
|
||||
{ method: 'DELETE' },
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* PUT /api/article/:sku/images/order
|
||||
* Body: { fullHashes: string[] } (every image exactly once)
|
||||
* → 200 { fullHashes: string[] }
|
||||
*/
|
||||
orderImages(sku, fullHashes) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/images/order`, {
|
||||
method: 'PUT',
|
||||
body: { fullHashes },
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* PUT /api/article/:sku/prices
|
||||
* Body: { prices: [{ price (net), tax (rate), minmultiplier? }] }
|
||||
* Replace-all POS customer group prices. Empty prices clears all POS prices.
|
||||
* → 200 { article } (same shape as GET by SKU)
|
||||
*/
|
||||
setPrices(sku, prices) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/prices`, {
|
||||
method: 'PUT',
|
||||
body: { prices },
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* PUT /api/article/:sku/weight
|
||||
* Body: { weight } → 200 { article }
|
||||
*/
|
||||
setWeight(sku, body) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/weight`, {
|
||||
method: 'PUT',
|
||||
body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* PUT /api/article/:sku/brand
|
||||
* Body: { brandId: number | null } — null clears the brand.
|
||||
* brandId must belong to the article's company → 404 Article or brand not found.
|
||||
* → 200 { article } (same shape as GET by SKU)
|
||||
*/
|
||||
setBrandIdBySku(sku, brandId) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/brand`, {
|
||||
method: 'PUT',
|
||||
body: { brandId: brandId != null ? Number(brandId) : null },
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* PUT /api/article/:sku/eans
|
||||
* Body: { eans: string[] } — replace-all. Empty array clears all.
|
||||
* EANs must be non-empty unique strings → 400 Invalid eans.
|
||||
* → 200 { article } (same shape as GET by SKU)
|
||||
*/
|
||||
setEansBySku(sku, eans) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/eans`, {
|
||||
method: 'PUT',
|
||||
body: { eans },
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* PUT /api/article/:sku/categories
|
||||
* Body: { categoryIds: number[] } — replace-all. Empty array clears all.
|
||||
* Categories must belong to the article's company; ids must be unique.
|
||||
* → 200 { article } (same shape as GET by SKU)
|
||||
*/
|
||||
setCategories(sku, categoryIds) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/categories`, {
|
||||
method: 'PUT',
|
||||
body: { categoryIds },
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* GET /api/article/:sku/supplier → { suppliers: ArticleSupplier[] }
|
||||
*/
|
||||
getSuppliers(sku) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/supplier`);
|
||||
},
|
||||
|
||||
/**
|
||||
* POST /api/article/:sku/supplier
|
||||
* Body: { entityId, sku?, pu?, stock?, sort?, netPrice?, taxId?, taxRate? }
|
||||
* → 201 { supplier: ArticleSupplier }
|
||||
*/
|
||||
addSupplier(sku, body) {
|
||||
const payload = {
|
||||
...body,
|
||||
entityId: Number(body?.entityId),
|
||||
};
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/supplier`, {
|
||||
method: 'POST',
|
||||
body: payload,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* PATCH /api/article/:sku/supplier/:id
|
||||
* Body: { sku?, pu?, stock?, sort?, netPrice?, taxId?, taxRate? }
|
||||
* → 200 { supplier: ArticleSupplier }
|
||||
*/
|
||||
updateSupplier(sku, id, body) {
|
||||
return http(
|
||||
`/api/article/${encodeURIComponent(sku)}/supplier/${encodeURIComponent(id)}`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
body,
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* PUT /api/article/:sku/supplier/order
|
||||
* Body: { ids: number[] }
|
||||
* → 200 { ids: number[] }
|
||||
*/
|
||||
orderSuppliers(sku, ids) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/supplier/order`, {
|
||||
method: 'PUT',
|
||||
body: { ids },
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* DELETE /api/article/:sku/supplier/:id → 204
|
||||
*/
|
||||
deleteSupplier(sku, id) {
|
||||
return http(
|
||||
`/api/article/${encodeURIComponent(sku)}/supplier/${encodeURIComponent(id)}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* GET /api/article/export.zip → application/zip (binary download)
|
||||
* `artikel.zip` = artikel.csv (semicolon-separated, Windows-1252) +
|
||||
* one `<fullHash>.avif` per article image (content-addressed, deduplicated).
|
||||
* Optional `categoryId` restricts the export to that category and below.
|
||||
* Errors: 400 No company selected, 401, 403, 404 (JSON { error }).
|
||||
*/
|
||||
exportZip(categoryId) {
|
||||
const params = new URLSearchParams();
|
||||
if (categoryId != null) params.set('categoryId', String(categoryId));
|
||||
const qs = params.toString();
|
||||
return httpGetBinary(`/api/article/export.zip${qs ? `?${qs}` : ''}`);
|
||||
},
|
||||
/**
|
||||
* POST /api/article/:sku/print
|
||||
* Body: { printerId, templateId, quantity }
|
||||
* Prints the given template to the specified printer for this article.
|
||||
*/
|
||||
printLabel(sku, body) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/print`, {
|
||||
method: 'POST',
|
||||
body,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* GET /api/article/:sku/attributevalue → { attributevalues: ArticleAttributeValue[] }
|
||||
*/
|
||||
getAttributeValues(sku) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/attributevalue`);
|
||||
},
|
||||
|
||||
/**
|
||||
* PUT /api/article/:sku/attributevalue
|
||||
* Body: { attributevalues: number[] | { attributeValueId: number, sort?: number }[] }
|
||||
* → 200 { attributevalues: ArticleAttributeValue[] }
|
||||
*/
|
||||
setAttributeValues(sku, attributevalues) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/attributevalue`, {
|
||||
method: 'PUT',
|
||||
body: { attributevalues },
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* GET /api/article/:sku/property → { properties: ArticleProperty[] }
|
||||
*/
|
||||
getProperties(sku) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/property`);
|
||||
},
|
||||
|
||||
/**
|
||||
* PUT /api/article/:sku/property
|
||||
* Body: { properties: { propertyId: number, value: string, sort?: number }[] }
|
||||
* → 200 { properties: ArticleProperty[] }
|
||||
*/
|
||||
setProperties(sku, properties) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/property`, {
|
||||
method: 'PUT',
|
||||
body: { properties },
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* DELETE /api/article/:sku/property/:propertyId → 200 { ok: true }
|
||||
*/
|
||||
deleteProperty(sku, propertyId) {
|
||||
return http(
|
||||
`/api/article/${encodeURIComponent(sku)}/property/${encodeURIComponent(propertyId)}`,
|
||||
{ method: 'DELETE' }
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* POST /api/article/:sku/parent
|
||||
* Body: { isParent: boolean, attributeNameId?: number }
|
||||
*/
|
||||
toggleParent(sku, body) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/parent`, {
|
||||
method: 'POST',
|
||||
body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* GET /api/article/:sku/children → { children: [...] }
|
||||
*/
|
||||
getChildren(sku) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/children`);
|
||||
},
|
||||
|
||||
/**
|
||||
* POST /api/article/:sku/children
|
||||
* Body: { attributeValueId?: number, value?: string, name?: string }
|
||||
*/
|
||||
addChild(sku, body) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/children`, {
|
||||
method: 'POST',
|
||||
body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* DELETE /api/article/:sku/children/:childSku
|
||||
*/
|
||||
removeChild(sku, childSku, options = {}) {
|
||||
const qs = options?.deleteArticle ? '?deleteArticle=true' : '';
|
||||
return http(
|
||||
`/api/article/${encodeURIComponent(sku)}/children/${encodeURIComponent(childSku)}${qs}`,
|
||||
{ method: 'DELETE' }
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* GET /api/article/:sku/articlelist → { items: [...] }
|
||||
*/
|
||||
getArticlelist(sku) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/articlelist`);
|
||||
},
|
||||
|
||||
/**
|
||||
* PUT /api/article/:sku/articlelist
|
||||
* Body: { items: [{ articleId, sku, count, sort }] }
|
||||
*/
|
||||
setArticlelist(sku, items) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/articlelist`, {
|
||||
method: 'PUT',
|
||||
body: Array.isArray(items) ? { items } : items,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* GET /api/article/:sku/optionslist → { options: [...] }
|
||||
*/
|
||||
getOptionslist(sku) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/optionslist`);
|
||||
},
|
||||
|
||||
/**
|
||||
* PUT /api/article/:sku/optionslist
|
||||
* Body: { options: [{ articleId, sku, count, sort, mandated }] }
|
||||
*/
|
||||
setOptionslist(sku, options) {
|
||||
return http(`/api/article/${encodeURIComponent(sku)}/optionslist`, {
|
||||
method: 'PUT',
|
||||
body: Array.isArray(options) ? { options } : options,
|
||||
});
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user