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 father ON father.id = a.fatherarticle_id `; export const ARTICLE_LIST_COLUMNS = ` a.id, a.sku, at.name, at.slug, pr.net, t.rate AS taxRate, a.track_stock, 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.fatherarticle_id = a.id ) AS childCount, a.fatherarticle_id AS fatherArticleId, father.sku AS fatherSku, pic.hash AS fullHash, pic.thumbw AS thumbW, pic.thumbh AS thumbH, pic.thumbhash AS thumbHash, 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 father/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 (duplicate guard). */ export const EAN_EXISTS = ` SELECT TOP 1 1 AS used FROM articleean WHERE ean = @ean `; /** Link an article to its father article and mark the father. */ export const UPDATE_FATHER_BY_SKU = ` UPDATE article SET fatherarticle_id = @fatherArticleId, fathersort = COALESCE(@fathersort, 0) WHERE sku = @sku AND (@companyId IS NULL OR company_id = @companyId) `; export const SET_IS_FATHER = ` UPDATE article SET is_father = 1 WHERE id = @articleId `; export const TOGGLE_FATHER = ` UPDATE article SET is_father = @isFather, fatherattributename_id = @fatherAttributeNameId WHERE id = @articleId `; export const CREATE_CHILD = ` INSERT INTO article ( company_id, sku, unit_id, unitamount, weight, track_stock, brand_id, is_father, fatherarticle_id, fathersort ) OUTPUT INSERTED.id VALUES ( @companyId, @sku, @unitId, @unitamount, @weight, @trackStock, @brandId, 0, @fatherArticleId, @fathersort ) `; export const UNLINK_CHILD = ` UPDATE article SET fatherarticle_id = NULL, fathersort = 0 WHERE id = @articleId AND fatherarticle_id = @fatherArticleId `; export const LIST_CHILDREN_BY_FATHER_ID = ` SELECT child.id, child.sku, child.fathersort, 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 ON aav.article_id = child.id LEFT JOIN attributevalue av ON av.id = aav.attributevalue_id AND av.attribute_id = @attributeId WHERE child.fatherarticle_id = @fatherArticleId ORDER BY child.fathersort ASC, child.id ASC `; export const SET_FATHER_SORT = ` UPDATE article SET fathersort = @fathersort WHERE id = @articleId AND fatherarticle_id = @fatherArticleId `; /** 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, 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.fatherarticle_id = a.id ) AS childCount, a.is_father AS isFather, a.fatherattributename_id AS fatherAttributeNameId, fan.name AS fatherAttributeName, fan.exclusive AS fatherAttributeExclusive, a.fatherarticle_id AS fatherArticleId, fa.sku AS fatherSku, ( SELECT child.id, child.sku, child.fathersort, 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 ON aav.article_id = child.id LEFT JOIN attributevalue av ON av.id = aav.attributevalue_id AND av.attribute_id = a.fatherattributename_id WHERE child.fatherarticle_id = a.id ORDER BY child.fathersort 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.price_id AS priceId, p.net AS [price.net], p.tax_id AS [price.taxId], t.rate AS [price.taxRate] FROM articlesupplier asup INNER JOIN entity e ON e.id = asup.entity_id LEFT JOIN price p ON p.id = asup.price_id LEFT JOIN tax t ON t.id = p.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, 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 = 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 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.fatherarticle_id LEFT JOIN attributename fan ON fan.id = a.fatherattributename_id WHERE a.id = auth.articleId FOR JSON PATH, WITHOUT_ARRAY_WRAPPER ) AS articleJson FROM auth `;