Files
dashlibstorage/src/providers/mssql/queries/article.js
2026-10-20 23:16:47 +02:00

747 lines
25 KiB
JavaScript

export const ARTICLE_LIST_JOINS = `
LEFT JOIN articletext at
ON at.article_id = a.id AND at.language_id = @languageId
LEFT JOIN price pr
ON pr.article_id = a.id
AND pr.customergroup_id = @posCustomerGroupId
AND pr.minmultiplier = 1
LEFT JOIN tax t ON t.id = pr.tax_id
OUTER APPLY (
SELECT TOP 1 ap.pic_id
FROM articlepic ap
WHERE ap.article_id = a.id
ORDER BY ap.sort ASC, ap.id ASC
) first_pic
LEFT JOIN pic ON pic.id = first_pic.pic_id
LEFT JOIN brand b ON b.id = a.brand_id
OUTER APPLY (
SELECT TOP 1 bp.pic_id
FROM brandpic bp
WHERE bp.brand_id = b.id
ORDER BY bp.sort ASC, bp.id ASC
) first_brand_pic
LEFT JOIN pic brand_pic ON brand_pic.id = first_brand_pic.pic_id
LEFT JOIN article parent ON parent.id = a.parentarticle_id
`;
export const ARTICLE_LIST_COLUMNS = `
a.id,
a.sku,
at.name,
at.slug,
pr.net,
t.rate AS taxRate,
a.track_stock,
a.is_parent AS isParent,
CASE
WHEN a.optionslist_id IS NOT NULL THEN (
SELECT COUNT(*)
FROM optionslistarticle ola
WHERE ola.optionslist_id = a.optionslist_id
)
ELSE 0
END AS optionsCount,
CASE
WHEN a.articlelist_id IS NOT NULL THEN (
SELECT COUNT(*)
FROM articlelistarticle ala
WHERE ala.articlelist_id = a.articlelist_id
)
ELSE 0
END AS articleListCount,
(
SELECT COUNT(*)
FROM article child
WHERE child.parentarticle_id = a.id
) AS childCount,
a.parentarticle_id AS parentArticleId,
parent.sku AS parentSku,
pic.hash AS fullHash,
pic.thumbw AS thumbW,
pic.thumbh AS thumbH,
pic.thumbhash AS thumbHash,
pic.smallthumbw AS smallThumbW,
pic.smallthumbh AS smallThumbH,
pic.smallthumbbinary AS smallThumbBinary,
b.id AS brandId,
b.name AS brandName,
brand_pic.hash AS brandFullHash,
brand_pic.thumbw AS brandThumbW,
brand_pic.thumbh AS brandThumbH,
brand_pic.thumbhash AS brandThumbHash
`;
export const GET_CATEGORY_ID_BY_SKU = `
SELECT TOP 1 ca.category_id AS categoryId, a.id AS articleId
FROM article a
INNER JOIN categoryarticle ca ON ca.article_id = a.id
WHERE a.sku = @sku AND (@companyId IS NULL OR a.company_id = @companyId)
`;
/** Resolve an article id by sku (used to wire parent/child compound links). */
export const GET_ID_BY_SKU = `
SELECT TOP 1 id AS articleId
FROM article
WHERE sku = @sku AND (@companyId IS NULL OR company_id = @companyId)
`;
/** True if an EAN is already assigned to any article in the company (duplicate guard). */
export const EAN_EXISTS = `
SELECT TOP 1 1 AS used
FROM articleean ae
INNER JOIN article a ON a.id = ae.article_id
WHERE ae.ean = @ean AND (@companyId IS NULL OR a.company_id = @companyId)
`;
export const EAN_EXISTS_FOR_OTHER_ARTICLE = `
SELECT TOP 1 1 AS used
FROM articleean ae
INNER JOIN article a ON a.id = ae.article_id
WHERE ae.ean = @ean AND a.company_id = @companyId AND a.id <> @articleId
`;
/** Link an article to its parent article and mark the parent. */
export const UPDATE_PARENT_BY_SKU = `
UPDATE article
SET parentarticle_id = @parentArticleId,
parentsort = COALESCE(@parentsort, 0)
WHERE sku = @sku AND (@companyId IS NULL OR company_id = @companyId)
`;
export const SET_IS_PARENT = `
UPDATE article
SET is_parent = 1
WHERE id = @articleId
`;
export const TOGGLE_PARENT = `
UPDATE article
SET is_parent = @isParent,
parentattributename_id = @parentAttributeNameId
WHERE id = @articleId
`;
export const CREATE_CHILD = `
INSERT INTO article (
company_id, sku, unit_id, unitamount, weight, track_stock, brand_id, is_parent, parentarticle_id, parentsort
)
OUTPUT INSERTED.id
VALUES (
@companyId, @sku, @unitId, @unitamount, @weight, @trackStock, @brandId, 0, @parentArticleId, @parentsort
)
`;
export const UNLINK_CHILD = `
UPDATE article
SET parentarticle_id = NULL,
parentsort = 0
WHERE id = @articleId AND parentarticle_id = @parentArticleId
`;
export const LIST_CHILDREN_BY_PARENT_ID = `
SELECT
child.id,
child.sku,
child.parentsort,
child.track_stock AS trackStock,
child.unitamount,
child.weight,
at_child.name,
at_child.slug,
av.id AS attributeValueId,
av.value AS attributeValue
FROM article child
LEFT JOIN articletext at_child ON at_child.article_id = child.id AND at_child.language_id = @languageId
LEFT JOIN (
articleattributevalue aav
INNER JOIN attributevalue av ON av.id = aav.attributevalue_id AND av.attribute_id = @attributeId
) ON aav.article_id = child.id
WHERE child.parentarticle_id = @parentArticleId
ORDER BY child.parentsort ASC, child.id ASC
`;
export const SET_PARENT_SORT = `
UPDATE article
SET parentsort = @parentsort
WHERE id = @articleId AND parentarticle_id = @parentArticleId
`;
/** SKU → article/category + company auth in one round-trip. */
export const RESOLVE_ARTICLE_ACCESS_BY_SKU = `
SELECT TOP 1
a.id AS articleId,
ca.category_id AS categoryId,
co.root_category_id AS rootCategoryId,
co.id AS companyId,
co.default_language_id AS languageId,
co.poscustomergroup_id AS posCustomerGroupId,
cu.id AS companyuserId
FROM article a
INNER JOIN company co ON co.id = a.company_id
INNER JOIN google_auth ga ON ga.id = @googleAuthId
INNER JOIN companyuser cu
ON cu.company_id = a.company_id AND cu.google_auth_id = @googleAuthId
LEFT JOIN categoryarticle ca ON ca.article_id = a.id
WHERE a.sku = @sku
AND (ga.selected_company IS NULL OR a.company_id = ga.selected_company)
`;
export const UPDATE_WEIGHT = `
UPDATE article
SET weight = @weight, updated_at = SYSUTCDATETIME()
WHERE id = @articleId
`;
export const UPDATE_TRACK_STOCK = `
UPDATE article
SET track_stock = @trackStock, updated_at = SYSUTCDATETIME()
WHERE id = @articleId
`;
export const UPDATE_BRAND = `
UPDATE article
SET brand_id = @brandId, updated_at = SYSUTCDATETIME()
WHERE id = @articleId
`;
export const UPDATE_UNIT = `
UPDATE article
SET unit_id = @unitId, updated_at = SYSUTCDATETIME()
WHERE id = @articleId
`;
export const UPDATE_UNITAMOUNT = `
UPDATE article
SET unitamount = @unitamount, updated_at = SYSUTCDATETIME()
WHERE id = @articleId
`;
export const UPDATE_UNIT_AND_AMOUNT = `
UPDATE article
SET unit_id = @unitId, unitamount = @unitamount, updated_at = SYSUTCDATETIME()
WHERE id = @articleId
`;
export const CREATE = `
INSERT INTO article (company_id, sku, unit_id, unitamount, weight, track_stock)
OUTPUT INSERTED.id
VALUES (@companyId, @sku, @unitId, @unitamount, @weight, @trackStock)
`;
export const DELETE_EANS_BY_ARTICLE = `
DELETE FROM articleean
WHERE article_id = @articleId
`;
export const ADD_EAN = `
INSERT INTO articleean (article_id, ean)
VALUES (@articleId, @ean)
`;
export const DELETE_CATEGORIES_BY_ARTICLE = `
DELETE FROM categoryarticle
WHERE article_id = @articleId
`;
export const ADD_CATEGORY = `
INSERT INTO categoryarticle (category_id, article_id)
VALUES (@categoryId, @articleId)
`;
export const GET_BRAND_BY_COMPANY_AND_ID = `
SELECT id
FROM brand
WHERE id = @brandId AND company_id = @companyId
`;
/**
* Company-wide article search index.
* Returns one row per article with id, sku, name, and all eans as a JSON array.
* Used by the client for local search / autocomplete.
*/
export const SEARCH_ARTICLES = `
;WITH category_tree AS (
SELECT id
FROM category
WHERE id = @rootCategoryId
UNION ALL
SELECT c.id
FROM category c
INNER JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT
a.id,
a.sku,
at.name,
(
SELECT ae.ean
FROM articleean ae
WHERE ae.article_id = a.id
ORDER BY ae.ean
FOR JSON PATH
) AS eansJson,
(
SELECT ca2.category_id
FROM categoryarticle ca2
WHERE ca2.article_id = a.id
ORDER BY ca2.category_id
FOR JSON PATH
) AS categoryIdsJson
FROM article a
LEFT JOIN articletext at
ON at.article_id = a.id AND at.language_id = @languageId
WHERE EXISTS (
SELECT 1
FROM categoryarticle ca
INNER JOIN category_tree ct ON ct.id = ca.category_id
WHERE ca.article_id = a.id
)
ORDER BY a.id
`;
/**
* Flat category tree (id, parentId, name) for a root category, used to resolve
* breadcrumbs for the article export.
*/
export const GET_CATEGORY_TREE_FLAT = `
;WITH tree AS (
SELECT id, parent_id, sort
FROM category
WHERE id = @rootId
UNION ALL
SELECT c.id, c.parent_id, c.sort
FROM category c
INNER JOIN tree t ON c.parent_id = t.id
)
SELECT
t.id,
t.parent_id AS parentId,
ct.name
FROM tree t
LEFT JOIN categorytext ct
ON ct.category_id = t.id AND ct.language_id = @languageId
`;
/**
* Export rows for every article in a category subtree (root = @categoryId).
* One row per article with the columns used by the export CSV, including the
* POS price (minmultiplier = 1 for the company poscustomergroup), brand, unit,
* eans, categoryIds and all photo hashes as JSON arrays.
*/
export const GET_ARTICLE_EXPORT = `
;WITH category_tree AS (
SELECT id
FROM category
WHERE id = @categoryId
UNION ALL
SELECT c.id
FROM category c
INNER JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT
a.id,
a.sku,
at.name,
at.slug,
u.si AS unitSi,
ut.symbol AS unitSymbol,
a.unitamount,
a.track_stock,
b.name AS brandName,
b.name AS brandName,
pr.net AS net,
t.rate AS taxRate,
(
SELECT ae.ean
FROM articleean ae
WHERE ae.article_id = a.id
ORDER BY ae.ean
FOR JSON PATH
) AS eansJson,
(
SELECT ca2.category_id
FROM categoryarticle ca2
WHERE ca2.article_id = a.id
ORDER BY ca2.category_id
FOR JSON PATH
) AS categoryIdsJson,
(
SELECT p.hash
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 fullHashesJson
FROM article a
INNER JOIN categoryarticle ca ON ca.article_id = a.id
INNER JOIN category_tree ct ON ct.id = ca.category_id
LEFT JOIN articletext at
ON at.article_id = a.id AND at.language_id = @languageId
LEFT JOIN unit u ON u.id = a.unit_id
LEFT JOIN unittext ut
ON ut.unit_id = u.id AND ut.language_id = @languageId
LEFT JOIN brand b ON b.id = a.brand_id
LEFT JOIN price pr
ON pr.article_id = a.id
AND pr.customergroup_id = @posCustomerGroupId
AND pr.minmultiplier = 1
LEFT JOIN tax t ON t.id = pr.tax_id
ORDER BY a.id
`;
/**
* Single-query replacement for resolveArticleBySku + queryBySku.
* Resolves SKU → article + company + auth, then loads full article detail
* including categories, photos, eans, and prices via FOR JSON sub-queries.
* Returns one row with articleJson, or empty if not found/unauthorized.
*/
export const GET_ARTICLE_BY_SKU_FULL = `
;WITH auth AS (
SELECT TOP 1
a.id AS articleId,
ca.category_id AS categoryId,
co.root_category_id AS rootCategoryId,
co.id AS companyId,
co.default_language_id AS languageId,
co.poscustomergroup_id AS posCustomerGroupId,
cu.id AS companyuserId
FROM article a
INNER JOIN company co ON co.id = a.company_id
INNER JOIN google_auth ga ON ga.id = @googleAuthId
INNER JOIN companyuser cu
ON cu.company_id = a.company_id AND cu.google_auth_id = @googleAuthId
LEFT JOIN categoryarticle ca ON ca.article_id = a.id
WHERE a.sku = @sku
AND (ga.selected_company IS NULL OR a.company_id = ga.selected_company)
)
SELECT
auth.articleId,
auth.categoryId,
auth.rootCategoryId,
auth.companyId,
auth.languageId,
auth.posCustomerGroupId,
auth.companyuserId,
(
SELECT
a.id,
a.sku,
at.name,
at.slug,
at.[desc] AS [desc],
at.seo AS seo,
a.unitamount,
a.weight,
a.track_stock,
u.si AS unitSi,
u.basesi AS unitBasesi,
u.basesifactor AS unitBasesifactor,
ut.symbol AS unitSymbol,
ut.name AS unitName,
b.id AS brandId,
b.name AS brandName,
bp.hash AS brandFullHash,
bp.thumbw AS brandThumbW,
bp.thumbh AS brandThumbH,
bp.thumbhash AS brandThumbHash,
(
SELECT COUNT(*)
FROM optionslistarticle ola
WHERE ola.optionslist_id = a.optionslist_id
) AS optionsCount,
a.optionslist_id AS optionslistId,
(
SELECT
ola.id,
ola.article_id AS articleId,
a_opt.sku,
at_opt.name,
ola.count,
ola.sort,
ola.mandated
FROM optionslistarticle ola
INNER JOIN article a_opt ON a_opt.id = ola.article_id
LEFT JOIN articletext at_opt ON at_opt.article_id = a_opt.id AND at_opt.language_id = auth.languageId
WHERE ola.optionslist_id = a.optionslist_id
ORDER BY ola.sort ASC, ola.id ASC
FOR JSON PATH
) AS optionslistItemsJson,
(
SELECT COUNT(*)
FROM articlelistarticle ala
WHERE ala.articlelist_id = a.articlelist_id
) AS articleListCount,
a.articlelist_id AS articlelistId,
(
SELECT
ala.id,
ala.article_id AS articleId,
a_item.sku,
at_item.name,
ala.count,
ala.sort
FROM articlelistarticle ala
INNER JOIN article a_item ON a_item.id = ala.article_id
LEFT JOIN articletext at_item ON at_item.article_id = a_item.id AND at_item.language_id = auth.languageId
WHERE ala.articlelist_id = a.articlelist_id
ORDER BY ala.sort ASC, ala.id ASC
FOR JSON PATH
) AS articlelistItemsJson,
(
SELECT COUNT(*)
FROM article child
WHERE child.parentarticle_id = a.id
) AS childCount,
a.is_parent AS isParent,
a.parentattributename_id AS parentAttributeNameId,
fan.name AS parentAttributeName,
fan.exclusive AS parentAttributeExclusive,
a.parentarticle_id AS parentArticleId,
fa.sku AS parentSku,
(
SELECT
child.id,
child.sku,
child.parentsort,
at_child.name,
av.id AS attributeValueId,
av.value AS attributeValue
FROM article child
LEFT JOIN articletext at_child ON at_child.article_id = child.id AND at_child.language_id = auth.languageId
LEFT JOIN (
articleattributevalue aav
INNER JOIN attributevalue av ON av.id = aav.attributevalue_id AND av.attribute_id = a.parentattributename_id
) ON aav.article_id = child.id
WHERE child.parentarticle_id = a.id
ORDER BY child.parentsort ASC, child.id ASC
FOR JSON PATH
) AS childrenJson,
(
SELECT ca2.category_id
FROM categoryarticle ca2
WHERE ca2.article_id = a.id
ORDER BY ca2.category_id
FOR JSON PATH
) AS categoryIdsJson,
(
SELECT
ap.sort,
p.hash AS fullHash,
p.w AS fullW,
p.h AS fullH,
p.size AS fullSize,
p.thumbw AS thumbW,
p.thumbh AS thumbH,
p.thumbhash AS thumbHash
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,
(
SELECT
pr.minmultiplier,
pr.net,
t.rate AS taxRate,
pr.customergroup_id AS customergroupId
FROM price pr
INNER JOIN tax t ON t.id = pr.tax_id
WHERE pr.article_id = a.id
ORDER BY pr.customergroup_id, pr.minmultiplier
FOR JSON PATH
) AS pricesJson,
(
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.net AS [price.net],
asup.tax_id AS [price.taxId],
t.rate AS [price.taxRate],
(
SELECT
m.[date] AS [date],
SUM(ma.amount - COALESCE(ma_ledger.booked_amount, 0)) AS amount
FROM manifest m
INNER JOIN manifestarticle ma ON ma.manifest_id = m.id
LEFT JOIN (
SELECT manifestarticle_id, SUM(amount) AS booked_amount
FROM ledger
WHERE warehouse_id IS NULL
GROUP BY manifestarticle_id
) ma_ledger ON ma_ledger.manifestarticle_id = ma.id
WHERE m.company_id = a.company_id
AND m.[type] = 'delivery'
AND m.entity_id = asup.entity_id
AND asup.sku IS NOT NULL
AND asup.sku <> ''
AND ma.sku = asup.sku
AND (ma.amount - COALESCE(ma_ledger.booked_amount, 0)) > 0
GROUP BY m.id, m.[date]
ORDER BY m.[date] ASC, m.id ASC
FOR JSON PATH
) AS incomingJson
FROM articlesupplier asup
INNER JOIN entity e ON e.id = asup.entity_id
LEFT JOIN tax t ON t.id = asup.tax_id
WHERE asup.article_id = a.id
ORDER BY asup.sort ASC, asup.id ASC
FOR JSON PATH
) AS suppliersJson,
(
SELECT
w.id,
w.name,
w.sort,
CASE
WHEN a.articlelist_id IS NOT NULL AND EXISTS (
SELECT 1 FROM articlelistarticle ala WHERE ala.articlelist_id = a.articlelist_id
) THEN COALESCE((
SELECT CASE
WHEN MIN(COALESCE(comp_stock.stock, 0) / ala.count) < 0 THEN 0
ELSE FLOOR(MIN(COALESCE(comp_stock.stock, 0) / ala.count))
END
FROM articlelistarticle ala
LEFT JOIN (
SELECT ca.id AS comp_id, SUM(l.amount) AS stock
FROM ledger l
INNER JOIN manifestarticle ma ON ma.id = l.manifestarticle_id
INNER JOIN manifest m ON m.id = ma.manifest_id
INNER JOIN article ca ON (
ma.sku = ca.sku
OR ma.sku IN (SELECT asup.sku FROM articlesupplier asup WHERE asup.article_id = ca.id)
)
WHERE m.company_id = a.company_id
AND l.warehouse_id = w.id
GROUP BY ca.id
) comp_stock ON comp_stock.comp_id = ala.article_id
WHERE ala.articlelist_id = a.articlelist_id
), 0)
WHEN a.articlelist_id IS NOT NULL THEN 0
ELSE COALESCE(stock_calc.stock, 0)
END AS stock
FROM warehouse w
LEFT JOIN (
SELECT l.warehouse_id, SUM(l.amount) AS stock
FROM ledger l
INNER JOIN manifestarticle ma ON ma.id = l.manifestarticle_id
INNER JOIN manifest m ON m.id = ma.manifest_id
WHERE m.company_id = a.company_id
AND (
ma.sku = a.sku
OR ma.sku IN (SELECT asup.sku FROM articlesupplier asup WHERE asup.article_id = a.id)
)
GROUP BY l.warehouse_id
) stock_calc ON stock_calc.warehouse_id = w.id
WHERE w.company_id = a.company_id
ORDER BY w.sort ASC, w.name ASC
FOR JSON PATH
) AS warehousesJson,
(
SELECT
m.id AS orderId,
m.[number] AS orderNumber,
m.[date] AS [date],
COALESCE(os.customer_name, LTRIM(RTRIM(ISNULL(e.first_name + ' ', '') + ISNULL(e.name, '')))) AS customerName,
ROUND(SUM(ma.amount) - COALESCE((
SELECT SUM(ma_s.amount)
FROM manifest m_s
INNER JOIN manifestarticle ma_s ON ma_s.manifest_id = m_s.id
LEFT JOIN shipmentsummary ss ON ss.manifest_id = m_s.id
WHERE m_s.company_id = m.company_id
AND m_s.[type] = 'shipment'
AND ISNULL(ss.state, 'new') != 'cancelled'
AND m_s.[number] NOT LIKE '%#cancelled#%'
AND ma_s.sku = a.sku
AND m_s.[number] LIKE m.[number] + COALESCE((SELECT prefix FROM numbers WHERE company_id = m.company_id AND [type] = 'shipment'), '-L-') + '%'
), 0), 3) AS amount
FROM manifest m
INNER JOIN manifestarticle ma ON ma.manifest_id = m.id
LEFT JOIN ordersummary os ON os.manifest_id = m.id
LEFT JOIN entity e ON e.id = m.entity_id
WHERE m.company_id = a.company_id
AND m.[type] = 'order'
AND ma.sku = a.sku
AND COALESCE(os.is_shipped, 0) = 0
GROUP BY m.id, m.[number], m.[date], os.customer_name, e.first_name, e.name
HAVING (SUM(ma.amount) - COALESCE((
SELECT SUM(ma_s.amount)
FROM manifest m_s
INNER JOIN manifestarticle ma_s ON ma_s.manifest_id = m_s.id
LEFT JOIN shipmentsummary ss ON ss.manifest_id = m_s.id
WHERE m_s.company_id = m.company_id
AND m_s.[type] = 'shipment'
AND ISNULL(ss.state, 'new') != 'cancelled'
AND m_s.[number] NOT LIKE '%#cancelled#%'
AND ma_s.sku = a.sku
AND m_s.[number] LIKE m.[number] + COALESCE((SELECT prefix FROM numbers WHERE company_id = m.company_id AND [type] = 'shipment'), '-L-') + '%'
), 0)) > 0
ORDER BY m.[date] ASC, m.id ASC
FOR JSON PATH
) AS undeliveredOrdersJson
FROM article a
LEFT JOIN articletext at
ON at.article_id = a.id AND at.language_id = auth.languageId
LEFT JOIN unit u ON u.id = a.unit_id
LEFT JOIN unittext ut
ON ut.unit_id = u.id AND ut.language_id = auth.languageId
LEFT JOIN brand b ON b.id = a.brand_id
OUTER APPLY (
SELECT TOP 1 bp2.pic_id
FROM brandpic bp2
WHERE bp2.brand_id = b.id
ORDER BY bp2.sort ASC, bp2.id ASC
) first_brand_pic
LEFT JOIN pic bp ON bp.id = first_brand_pic.pic_id
LEFT JOIN article fa ON fa.id = a.parentarticle_id
LEFT JOIN attributename fan ON fan.id = a.parentattributename_id
WHERE a.id = auth.articleId
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
) AS articleJson
FROM auth
`;
export const CHECK_HAS_WAREHOUSE = `
SELECT TOP 1 id
FROM warehouse
WHERE company_id = @companyId;
`;
export const CHECK_ARTICLE_WAREHOUSE_STOCKS = `
SELECT w.id AS warehouseId, w.name AS warehouseName, COALESCE(stock_calc.stock, 0) AS stock
FROM warehouse w
LEFT JOIN (
SELECT l.warehouse_id, SUM(l.amount) AS stock
FROM ledger l
INNER JOIN manifestarticle ma ON ma.id = l.manifestarticle_id
INNER JOIN manifest m ON m.id = ma.manifest_id
WHERE m.company_id = @companyId
AND (
ma.sku = @sku
OR ma.sku IN (SELECT asup.sku FROM articlesupplier asup WHERE asup.article_id = @articleId)
)
GROUP BY l.warehouse_id
) stock_calc ON stock_calc.warehouse_id = w.id
WHERE w.company_id = @companyId;
`;