feat(articlesupplier): add listByEntityAndSkus API with linked article, photos, and eans
This commit is contained in:
@@ -100,6 +100,35 @@ const suppliers = await store.articlesupplier.listByArticleSku(42, 'SKU-1');
|
|||||||
// => [{ id, articleId, entityId, supplierName, sku, pu, stock, priceId, price }]
|
// => [{ id, articleId, entityId, supplierName, sku, pu, stock, priceId, price }]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `articlesupplier.listByEntityAndSkus`
|
||||||
|
|
||||||
|
*Source: `src/store/articlesupplier/listByEntityAndSkus.js`*
|
||||||
|
|
||||||
|
Lists article-supplier assignments for a given supplier (entityId) and SKU list, including linked article details (name, slug, photos/articlepic, eans/articleean).
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||||
|
| `entityIdOrOptions` | `number|{entityId?: number, supplierId?: number, skus?: string[]|string}` | The supplier entity id or an options object. |
|
||||||
|
| `[maybeSkus]` | `string[]|string` | The list of SKUs to filter by (or single SKU string). |
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
|
||||||
|
`Promise<Array<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null, article: {id: number, sku: string, name: string|null, slug: string|null, photos: Array<{sort: number, thumbhash: string, hash: string, thumbw: number, thumbh: number}>, articlepic: Array<{sort: number, thumbhash: string, hash: string, thumbw: number, thumbh: number}>, eans: string[], articleean: string[]}}>>` — The supplier assignments with article details.
|
||||||
|
|
||||||
|
**Throws**
|
||||||
|
|
||||||
|
`Error` — If user is not authorized, entityId is missing, or supplier does not exist.
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
```js
|
||||||
|
const items = await store.articlesupplier.listByEntityAndSkus(42, 5, ['SKU-1', 'SKU-2']);
|
||||||
|
// => [{ id, articleId, entityId, supplierName, sku, pu, stock, sort, price, article: { id, sku, name, slug, photos, eans } }]
|
||||||
|
```
|
||||||
|
|
||||||
## `articlesupplier.order`
|
## `articlesupplier.order`
|
||||||
|
|
||||||
*Source: `src/store/articlesupplier/order.js`*
|
*Source: `src/store/articlesupplier/order.js`*
|
||||||
|
|||||||
1
API.md
1
API.md
@@ -102,6 +102,7 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
|
|||||||
- `create(googleAuthId: number, sku: string, input: {entityId: number, sku?: string, pu?: number, stock?: number|null, sort?: number, netPrice?: number|null, taxId?: number, taxRate?: number}): Promise<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null}>`
|
- `create(googleAuthId: number, sku: string, input: {entityId: number, sku?: string, pu?: number, stock?: number|null, sort?: number, netPrice?: number|null, taxId?: number, taxRate?: number}): Promise<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null}>`
|
||||||
- `delete(googleAuthId: number, sku: string, id: number): Promise<void>`
|
- `delete(googleAuthId: number, sku: string, id: number): Promise<void>`
|
||||||
- `listByArticleSku(googleAuthId: number, sku: string): Promise<Array<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null}>>`
|
- `listByArticleSku(googleAuthId: number, sku: string): Promise<Array<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null}>>`
|
||||||
|
- `listByEntityAndSkus(googleAuthId: number, entityIdOrOptions: number|{entityId?: number, supplierId?: number, skus?: string[]|string}, maybeSkus?: string[]|string): Promise<Array<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null, article: {id: number, sku: string, name: string|null, slug: string|null, photos: Array<{sort: number, thumbhash: string, hash: string, thumbw: number, thumbh: number}>, articlepic: Array<{sort: number, thumbhash: string, hash: string, thumbw: number, thumbh: number}>, eans: string[], articleean: string[]}}>>`
|
||||||
- `order(googleAuthId: number, sku: string, input: {ids: Array<number>}): Promise<{ids: Array<number>}>`
|
- `order(googleAuthId: number, sku: string, input: {ids: Array<number>}): Promise<{ids: Array<number>}>`
|
||||||
- `update(googleAuthId: number, sku: string, id: number, patch: {sku?: string|null, pu?: number, stock?: number|null, sort?: number, netPrice?: number|null, taxId?: number, taxRate?: number}): Promise<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null}>`
|
- `update(googleAuthId: number, sku: string, id: number, patch: {sku?: string|null, pu?: number, stock?: number|null, sort?: number, netPrice?: number|null, taxId?: number, taxRate?: number}): Promise<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null}>`
|
||||||
- [delivery](API.delivery.md)
|
- [delivery](API.delivery.md)
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ import supplierCreate from './store/supplier/create.js';
|
|||||||
import supplierUpdate from './store/supplier/update.js';
|
import supplierUpdate from './store/supplier/update.js';
|
||||||
import supplierDelete from './store/supplier/delete.js';
|
import supplierDelete from './store/supplier/delete.js';
|
||||||
import articlesupplierListByArticleSku from './store/articlesupplier/listByArticleSku.js';
|
import articlesupplierListByArticleSku from './store/articlesupplier/listByArticleSku.js';
|
||||||
|
import articlesupplierListByEntityAndSkus from './store/articlesupplier/listByEntityAndSkus.js';
|
||||||
import articlesupplierCreate from './store/articlesupplier/create.js';
|
import articlesupplierCreate from './store/articlesupplier/create.js';
|
||||||
import articlesupplierUpdate from './store/articlesupplier/update.js';
|
import articlesupplierUpdate from './store/articlesupplier/update.js';
|
||||||
import articlesupplierDelete from './store/articlesupplier/delete.js';
|
import articlesupplierDelete from './store/articlesupplier/delete.js';
|
||||||
@@ -245,6 +246,8 @@ export default class LibDashStore {
|
|||||||
|
|
||||||
this.articlesupplier = {
|
this.articlesupplier = {
|
||||||
listByArticleSku: articlesupplierListByArticleSku(pool),
|
listByArticleSku: articlesupplierListByArticleSku(pool),
|
||||||
|
listByEntityAndSkus: articlesupplierListByEntityAndSkus(pool),
|
||||||
|
listBySupplierAndSkus: articlesupplierListByEntityAndSkus(pool),
|
||||||
create: articlesupplierCreate(pool),
|
create: articlesupplierCreate(pool),
|
||||||
update: articlesupplierUpdate(pool),
|
update: articlesupplierUpdate(pool),
|
||||||
order: articlesupplierOrder(pool),
|
order: articlesupplierOrder(pool),
|
||||||
|
|||||||
@@ -117,3 +117,58 @@ SELECT taxrate
|
|||||||
FROM company
|
FROM company
|
||||||
WHERE id = @companyId;
|
WHERE id = @companyId;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const LIST_BY_ENTITY_AND_SKUS = `
|
||||||
|
SELECT
|
||||||
|
asup.id,
|
||||||
|
asup.article_id AS articleId,
|
||||||
|
asup.entity_id AS entityId,
|
||||||
|
e.name AS supplierName,
|
||||||
|
asup.sku,
|
||||||
|
asup.pu,
|
||||||
|
asup.stock,
|
||||||
|
asup.sort,
|
||||||
|
asup.price_id AS priceId,
|
||||||
|
p.net AS priceNet,
|
||||||
|
p.tax_id AS priceTaxId,
|
||||||
|
t.rate AS priceTaxRate,
|
||||||
|
a.id AS a_id,
|
||||||
|
a.sku AS a_sku,
|
||||||
|
at.name AS a_name,
|
||||||
|
at.slug AS a_slug,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
ap.sort,
|
||||||
|
p.hash,
|
||||||
|
p.thumbhash,
|
||||||
|
p.thumbw,
|
||||||
|
p.thumbh
|
||||||
|
FROM articlepic ap
|
||||||
|
INNER JOIN pic p ON p.id = ap.pic_id
|
||||||
|
WHERE ap.article_id = a.id
|
||||||
|
ORDER BY ap.sort ASC, ap.id ASC
|
||||||
|
FOR JSON PATH
|
||||||
|
) AS photosJson,
|
||||||
|
(
|
||||||
|
SELECT ae.ean
|
||||||
|
FROM articleean ae
|
||||||
|
WHERE ae.article_id = a.id
|
||||||
|
ORDER BY ae.ean
|
||||||
|
FOR JSON PATH
|
||||||
|
) AS eansJson
|
||||||
|
FROM articlesupplier asup
|
||||||
|
INNER JOIN entity e ON e.id = asup.entity_id
|
||||||
|
INNER JOIN customergroup cg ON cg.id = e.customergroup_id
|
||||||
|
INNER JOIN article a ON a.id = asup.article_id
|
||||||
|
LEFT JOIN articletext at ON at.article_id = a.id AND at.language_id = @languageId
|
||||||
|
LEFT JOIN price p ON p.id = asup.price_id
|
||||||
|
LEFT JOIN tax t ON t.id = p.tax_id
|
||||||
|
WHERE asup.entity_id = @entityId
|
||||||
|
AND cg.company_id = @companyId
|
||||||
|
AND (
|
||||||
|
@skusJson IS NULL
|
||||||
|
OR a.sku IN (SELECT value FROM OPENJSON(@skusJson))
|
||||||
|
OR asup.sku IN (SELECT value FROM OPENJSON(@skusJson))
|
||||||
|
)
|
||||||
|
ORDER BY asup.sort ASC, asup.id ASC;
|
||||||
|
`;
|
||||||
|
|||||||
130
src/store/articlesupplier/listByEntityAndSkus.js
Normal file
130
src/store/articlesupplier/listByEntityAndSkus.js
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
import { LIST_BY_ENTITY_AND_SKUS } from '../../sql/articlesupplier.js';
|
||||||
|
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||||
|
import { assertSupplier } from './helpers.js';
|
||||||
|
|
||||||
|
function parseJsonArray(raw) {
|
||||||
|
if (!raw) return [];
|
||||||
|
try {
|
||||||
|
const val = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
||||||
|
return Array.isArray(val) ? val : [];
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function listByEntityAndSkus(pool) {
|
||||||
|
const resolveAccess = resolveSelectedCompanyAccess(pool);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists article-supplier assignments for a given supplier (entityId) and SKU list,
|
||||||
|
* including linked article details (name, slug, photos/articlepic, eans/articleean).
|
||||||
|
*
|
||||||
|
* @param {number} googleAuthId - The id of the Google auth user.
|
||||||
|
* @param {number|{entityId?: number, supplierId?: number, skus?: string[]|string}} entityIdOrOptions - The supplier entity id or an options object.
|
||||||
|
* @param {string[]|string} [maybeSkus] - The list of SKUs to filter by (or single SKU string).
|
||||||
|
* @returns {Promise<Array<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, priceId: number|null, price: {net: number, taxId: number, taxRate?: number}|null, article: {id: number, sku: string, name: string|null, slug: string|null, photos: Array<{sort: number, thumbhash: string, hash: string, thumbw: number, thumbh: number}>, articlepic: Array<{sort: number, thumbhash: string, hash: string, thumbw: number, thumbh: number}>, eans: string[], articleean: string[]}}>>} The supplier assignments with article details.
|
||||||
|
* @throws {Error} If user is not authorized, entityId is missing, or supplier does not exist.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const items = await store.articlesupplier.listByEntityAndSkus(42, 5, ['SKU-1', 'SKU-2']);
|
||||||
|
* // => [{ id, articleId, entityId, supplierName, sku, pu, stock, sort, price, article: { id, sku, name, slug, photos, eans } }]
|
||||||
|
*/
|
||||||
|
return async (googleAuthId, entityIdOrOptions, maybeSkus) => {
|
||||||
|
let entityId;
|
||||||
|
let skus;
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof entityIdOrOptions === 'object' &&
|
||||||
|
entityIdOrOptions !== null &&
|
||||||
|
!Array.isArray(entityIdOrOptions)
|
||||||
|
) {
|
||||||
|
entityId = entityIdOrOptions.entityId ?? entityIdOrOptions.supplierId;
|
||||||
|
skus = entityIdOrOptions.skus;
|
||||||
|
} else {
|
||||||
|
entityId = entityIdOrOptions;
|
||||||
|
skus = maybeSkus;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entityId == null || !Number.isFinite(Number(entityId))) {
|
||||||
|
throw new Error('Supplier required');
|
||||||
|
}
|
||||||
|
const parsedEntityId = Number(entityId);
|
||||||
|
|
||||||
|
// Fast return if an empty array of skus was explicitly requested
|
||||||
|
if (Array.isArray(skus) && skus.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let skusJson = null;
|
||||||
|
if (skus != null) {
|
||||||
|
const rawList = Array.isArray(skus) ? skus : [skus];
|
||||||
|
const cleanList = rawList
|
||||||
|
.map((s) => (s == null ? '' : String(s).trim()))
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
if (cleanList.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
skusJson = JSON.stringify(Array.from(new Set(cleanList)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const { companyId, languageId } = await resolveAccess(googleAuthId);
|
||||||
|
|
||||||
|
await assertSupplier(pool, null, companyId, parsedEntityId);
|
||||||
|
|
||||||
|
const result = await pool
|
||||||
|
.request()
|
||||||
|
.input('entityId', parsedEntityId)
|
||||||
|
.input('companyId', companyId)
|
||||||
|
.input('languageId', languageId)
|
||||||
|
.input('skusJson', skusJson)
|
||||||
|
.query(LIST_BY_ENTITY_AND_SKUS);
|
||||||
|
|
||||||
|
return result.recordset.map((row) => {
|
||||||
|
const rawPhotos = parseJsonArray(row.photosJson);
|
||||||
|
const photos = rawPhotos.map((p) => ({
|
||||||
|
sort: p.sort,
|
||||||
|
thumbhash: p.thumbhash,
|
||||||
|
hash: p.hash,
|
||||||
|
thumbw: p.thumbw,
|
||||||
|
thumbh: p.thumbh,
|
||||||
|
thumbHash: p.thumbhash,
|
||||||
|
fullHash: p.hash,
|
||||||
|
thumbW: p.thumbw,
|
||||||
|
thumbH: p.thumbh,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const eans = parseJsonArray(row.eansJson).map((r) => r.ean);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: row.id,
|
||||||
|
articleId: row.articleId,
|
||||||
|
entityId: row.entityId,
|
||||||
|
supplierName: row.supplierName ?? null,
|
||||||
|
sku: row.sku ?? null,
|
||||||
|
pu: row.pu,
|
||||||
|
stock: row.stock ?? null,
|
||||||
|
sort: row.sort,
|
||||||
|
priceId: row.priceId ?? null,
|
||||||
|
price:
|
||||||
|
row.priceId != null && row.priceNet != null
|
||||||
|
? {
|
||||||
|
net: row.priceNet,
|
||||||
|
taxId: row.priceTaxId,
|
||||||
|
...(row.priceTaxRate != null ? { taxRate: row.priceTaxRate } : {}),
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
article: {
|
||||||
|
id: row.a_id,
|
||||||
|
sku: row.a_sku,
|
||||||
|
name: row.a_name ?? null,
|
||||||
|
slug: row.a_slug ?? null,
|
||||||
|
photos,
|
||||||
|
articlepic: photos,
|
||||||
|
eans,
|
||||||
|
articleean: eans,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user