feat(articlesupplier): add listByEntityAndSkus API with linked article, photos, and eans
This commit is contained in:
@@ -66,6 +66,7 @@ import supplierCreate from './store/supplier/create.js';
|
||||
import supplierUpdate from './store/supplier/update.js';
|
||||
import supplierDelete from './store/supplier/delete.js';
|
||||
import articlesupplierListByArticleSku from './store/articlesupplier/listByArticleSku.js';
|
||||
import articlesupplierListByEntityAndSkus from './store/articlesupplier/listByEntityAndSkus.js';
|
||||
import articlesupplierCreate from './store/articlesupplier/create.js';
|
||||
import articlesupplierUpdate from './store/articlesupplier/update.js';
|
||||
import articlesupplierDelete from './store/articlesupplier/delete.js';
|
||||
@@ -245,6 +246,8 @@ export default class LibDashStore {
|
||||
|
||||
this.articlesupplier = {
|
||||
listByArticleSku: articlesupplierListByArticleSku(pool),
|
||||
listByEntityAndSkus: articlesupplierListByEntityAndSkus(pool),
|
||||
listBySupplierAndSkus: articlesupplierListByEntityAndSkus(pool),
|
||||
create: articlesupplierCreate(pool),
|
||||
update: articlesupplierUpdate(pool),
|
||||
order: articlesupplierOrder(pool),
|
||||
|
||||
@@ -117,3 +117,58 @@ SELECT taxrate
|
||||
FROM company
|
||||
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