This commit is contained in:
seb
2026-09-30 17:35:22 +02:00
parent 280a45a11f
commit 894f72eba5
11 changed files with 802 additions and 10 deletions

View File

@@ -111,6 +111,8 @@ import posterminalGetCustomerGroupCount from './store/posterminal/getCustomerGro
import posterminalGetCompositeProductCount from './store/posterminal/getCompositeProductCount.js';
import posterminalGetDeletedEntityCount from './store/posterminal/getDeletedEntityCount.js';
import posterminalGetMaxOrderIdCount from './store/posterminal/getMaxOrderIdCount.js';
import posterminalGetCategoryList from './store/posterminal/getCategoryList.js';
import posterminalGetProductList from './store/posterminal/getProductList.js';
import langCreate from './store/lang/create.js';
import langDelete from './store/lang/delete.js';
import langList from './store/lang/list.js';
@@ -384,6 +386,8 @@ export default class LibDashStore {
getCompositeProductCount: posterminalGetCompositeProductCount(provider),
getDeletedEntityCount: posterminalGetDeletedEntityCount(provider),
getMaxOrderIdCount: posterminalGetMaxOrderIdCount(provider),
getCategoryList: posterminalGetCategoryList(provider),
getProductList: posterminalGetProductList(provider),
};
this.google_auth = {

View File

@@ -574,7 +574,33 @@ SELECT
w.id,
w.name,
w.sort,
COALESCE(stock_calc.stock, 0) AS stock
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

View File

@@ -195,3 +195,182 @@ FROM manifest m
INNER JOIN posterminal pt ON pt.id = @posterminalId AND pt.company_id = m.company_id
WHERE m.[type] = 'order'
`;
export const LIST_POS_CATEGORIES = `
SELECT TOP (@limit)
c.id,
c.parent_id AS pid,
c.sort,
COALESCE(ct.name, '') AS name,
co.root_category_id AS rootCategoryId,
(
SELECT TOP 1 p.hash
FROM categorypic cp
INNER JOIN pic p ON p.id = cp.pic_id
WHERE cp.category_id = c.id
ORDER BY cp.sort ASC, cp.id ASC
) AS imgHash,
DATEDIFF_BIG(second, '1970-01-01', c.updated_at) AS lastChanged
FROM category c
INNER JOIN posterminalcategory pc ON pc.category_id = c.id
INNER JOIN posterminal pt ON pt.id = pc.posterminal_id
INNER JOIN company co ON co.id = pt.company_id
LEFT JOIN categorytext ct ON ct.category_id = c.id AND ct.language_id = co.default_language_id
WHERE pc.posterminal_id = @posterminalId
AND (co.root_category_id IS NULL OR c.id <> co.root_category_id)
AND DATEDIFF_BIG(second, '1970-01-01', c.updated_at) > @cursor
ORDER BY DATEDIFF_BIG(second, '1970-01-01', c.updated_at) ASC, c.id ASC
`;
export const LIST_POS_PRODUCTS = `
SELECT TOP (@limit)
a.id,
a.sku,
COALESCE(at.name, a.sku, '') AS name,
COALESCE(pr.net, 0) AS netPrice,
COALESCE(ptax.rate, ctax.rate, 0) AS taxRate,
a.created_at AS createdAt,
DATEDIFF_BIG(second, '1970-01-01', a.updated_at) AS lastChanged,
COALESCE(
(
SELECT TOP 1 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
),
(
SELECT TOP 1 p2.hash
FROM articlepic ap2
INNER JOIN pic p2 ON p2.id = ap2.pic_id
WHERE a.fatherarticle_id IS NOT NULL AND ap2.article_id = a.fatherarticle_id
ORDER BY ap2.sort ASC, ap2.id ASC
)
) AS imgHash,
(
SELECT TOP 1 ae.ean
FROM articleean ae
WHERE ae.article_id = a.id
ORDER BY ae.id ASC
) AS barcode,
a.is_father AS isParent,
a.fatherarticle_id AS parentArticleId,
(
SELECT TOP 1 CONCAT(fan.name, '|', av.value, '|', fan.sort, '|', a.fathersort)
FROM article fa
INNER JOIN attributename fan ON fan.id = fa.fatherattributename_id
INNER JOIN articleattributevalue aav ON aav.article_id = a.id
INNER JOIN attributevalue av ON av.id = aav.attributevalue_id AND av.attribute_id = fan.id
WHERE a.fatherarticle_id IS NOT NULL AND fa.id = a.fatherarticle_id
) AS variantName,
CASE
WHEN a.articlelist_id IS NOT NULL AND EXISTS (
SELECT 1 FROM articlelistarticle ala WHERE ala.articlelist_id = a.articlelist_id
) THEN '1'
ELSE '0'
END AS isCompositeProduct,
(
SELECT COUNT(*)
FROM categoryarticle ca2
INNER JOIN article a2 ON a2.id = ca2.article_id
LEFT JOIN articletext at2 ON at2.article_id = a2.id AND at2.language_id = co.default_language_id
WHERE ca2.category_id = (
SELECT MIN(ca1.category_id)
FROM categoryarticle ca1
WHERE ca1.article_id = a.id
)
AND (
COALESCE(at2.name, a2.sku, '') < COALESCE(at.name, a.sku, '')
OR (COALESCE(at2.name, a2.sku, '') = COALESCE(at.name, a.sku, '') AND a2.id < a.id)
)
) AS sort,
a.track_stock AS trackStock,
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
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((
SELECT SUM(l.amount)
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)
)
), 0)
END AS fBestand,
co.id AS companyId,
co.root_category_id AS rootCategoryId,
co.poscustomergroup_id AS posCustomerGroupId
FROM article a
INNER JOIN posterminal pt ON pt.id = @posterminalId AND pt.company_id = a.company_id
INNER JOIN company co ON co.id = a.company_id
LEFT JOIN articletext at ON at.article_id = a.id AND at.language_id = co.default_language_id
LEFT JOIN price pr ON pr.article_id = a.id AND pr.customergroup_id = co.poscustomergroup_id AND pr.minmultiplier = 1
LEFT JOIN tax ptax ON ptax.id = pr.tax_id
LEFT JOIN tax ctax ON ctax.id = co.taxrate
WHERE EXISTS (
SELECT 1
FROM categoryarticle ca
INNER JOIN posterminalcategory pc ON pc.category_id = ca.category_id
WHERE ca.article_id = a.id
AND pc.posterminal_id = @posterminalId
)
AND DATEDIFF_BIG(second, '1970-01-01', a.updated_at) > @cursor
ORDER BY DATEDIFF_BIG(second, '1970-01-01', a.updated_at) ASC, a.id ASC
`;
export const LIST_POS_PRODUCT_CATEGORIES = `
SELECT
ca.article_id AS articleId,
ca.category_id AS categoryId
FROM categoryarticle ca
INNER JOIN posterminalcategory pc ON pc.category_id = ca.category_id
INNER JOIN posterminal pt ON pt.id = pc.posterminal_id
INNER JOIN company co ON co.id = pt.company_id
WHERE pc.posterminal_id = @posterminalId
AND (co.root_category_id IS NULL OR ca.category_id <> co.root_category_id)
ORDER BY ca.article_id ASC, ca.category_id ASC
`;
export const LIST_POS_CUSTOMER_GROUP_IDS = `
SELECT cg.id
FROM customergroup cg
INNER JOIN posterminal pt ON pt.id = @posterminalId AND pt.company_id = cg.company_id
WHERE cg.name <> 'Lieferanten'
ORDER BY cg.id ASC
`;
export const LIST_POS_PRODUCT_PRICES = `
SELECT
pr.article_id AS articleId,
pr.customergroup_id AS customerGroupId,
MIN(pr.net) AS netPrice
FROM price pr
INNER JOIN article a ON a.id = pr.article_id
INNER JOIN posterminal pt ON pt.id = @posterminalId AND pt.company_id = a.company_id
WHERE pr.minmultiplier = 1
GROUP BY pr.article_id, pr.customergroup_id
`;

View File

@@ -215,7 +215,34 @@ SELECT auth.*,
WHERE s.article_id = a.id ORDER BY s.sort, s.id
))),
'warehousesJson', json((SELECT COALESCE(json_group_array(json_object(
'id', w.id, 'name', w.name, 'sort', w.sort, 'stock', COALESCE(stock_calc.stock, 0)
'id', w.id, 'name', w.name, 'sort', w.sort,
'stock', 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 CAST(MIN(COALESCE(comp_stock.stock, 0) / ala.count) AS INTEGER)
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
)), '[]') FROM warehouse w
LEFT JOIN (
SELECT l.warehouse_id, SUM(l.amount) AS stock

View File

@@ -8,6 +8,7 @@ export const {
FIND_BY_PAIRINGPIN, PAIR,
FIND_BY_TOKEN, COUNT_PRODUCTS, COUNT_CATEGORIES, COUNT_CUSTOMERS,
COUNT_CUSTOMER_GROUPS, COUNT_COMPOSITE_PRODUCTS, MAX_ORDER_ID,
LIST_POS_PRODUCT_CATEGORIES, LIST_POS_CUSTOMER_GROUP_IDS, LIST_POS_PRODUCT_PRICES,
} = queries;
export const SET_ON = `
@@ -25,4 +26,157 @@ WITH RECURSIVE cats AS (
INSERT INTO posterminalcategory (posterminal_id, category_id)
SELECT @posterminalId, id FROM cats WHERE 1
ON CONFLICT (posterminal_id, category_id) DO NOTHING
`;
`;
export const LIST_POS_CATEGORIES = `
SELECT
c.id,
c.parent_id AS pid,
c.sort,
COALESCE(ct.name, '') AS name,
co.root_category_id AS rootCategoryId,
(
SELECT p.hash
FROM categorypic cp
INNER JOIN pic p ON p.id = cp.pic_id
WHERE cp.category_id = c.id
ORDER BY cp.sort ASC, cp.id ASC
LIMIT 1
) AS imgHash,
CAST(strftime('%s', c.updated_at) AS INTEGER) AS lastChanged
FROM category c
INNER JOIN posterminalcategory pc ON pc.category_id = c.id
INNER JOIN posterminal pt ON pt.id = pc.posterminal_id
INNER JOIN company co ON co.id = pt.company_id
LEFT JOIN categorytext ct ON ct.category_id = c.id AND ct.language_id = co.default_language_id
WHERE pc.posterminal_id = @posterminalId
AND (co.root_category_id IS NULL OR c.id <> co.root_category_id)
AND CAST(strftime('%s', c.updated_at) AS INTEGER) > @cursor
ORDER BY lastChanged ASC, c.id ASC
LIMIT @limit
`;
export const LIST_POS_PRODUCTS = `
SELECT
a.id,
a.sku,
COALESCE(at.name, a.sku, '') AS name,
COALESCE(pr.net, 0) AS netPrice,
COALESCE(ptax.rate, ctax.rate, 0) AS taxRate,
a.created_at AS createdAt,
CAST(strftime('%s', a.updated_at) AS INTEGER) AS lastChanged,
COALESCE(
(
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
LIMIT 1
),
(
SELECT p2.hash
FROM articlepic ap2
INNER JOIN pic p2 ON p2.id = ap2.pic_id
WHERE a.fatherarticle_id IS NOT NULL AND ap2.article_id = a.fatherarticle_id
ORDER BY ap2.sort ASC, ap2.id ASC
LIMIT 1
)
) AS imgHash,
(
SELECT ae.ean
FROM articleean ae
WHERE ae.article_id = a.id
ORDER BY ae.id ASC
LIMIT 1
) AS barcode,
a.is_father AS isParent,
a.fatherarticle_id AS parentArticleId,
(
SELECT fan.name || '|' || av.value || '|' || fan.sort || '|' || a.fathersort
FROM article fa
INNER JOIN attributename fan ON fan.id = fa.fatherattributename_id
INNER JOIN articleattributevalue aav ON aav.article_id = a.id
INNER JOIN attributevalue av ON av.id = aav.attributevalue_id AND av.attribute_id = fan.id
WHERE a.fatherarticle_id IS NOT NULL AND fa.id = a.fatherarticle_id
LIMIT 1
) AS variantName,
CASE
WHEN a.articlelist_id IS NOT NULL AND EXISTS (
SELECT 1 FROM articlelistarticle ala WHERE ala.articlelist_id = a.articlelist_id
) THEN '1'
ELSE '0'
END AS isCompositeProduct,
(
SELECT COUNT(*)
FROM categoryarticle ca2
INNER JOIN article a2 ON a2.id = ca2.article_id
LEFT JOIN articletext at2 ON at2.article_id = a2.id AND at2.language_id = co.default_language_id
WHERE ca2.category_id = (
SELECT MIN(ca1.category_id)
FROM categoryarticle ca1
WHERE ca1.article_id = a.id
)
AND (
COALESCE(at2.name, a2.sku, '') < COALESCE(at.name, a.sku, '')
OR (COALESCE(at2.name, a2.sku, '') = COALESCE(at.name, a.sku, '') AND a2.id < a.id)
)
) AS sort,
a.track_stock AS trackStock,
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 CAST(MIN(COALESCE(comp_stock.stock, 0) / ala.count) AS INTEGER)
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
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((
SELECT SUM(l.amount)
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)
)
), 0)
END AS fBestand,
co.id AS companyId,
co.root_category_id AS rootCategoryId,
co.poscustomergroup_id AS posCustomerGroupId
FROM article a
INNER JOIN posterminal pt ON pt.id = @posterminalId AND pt.company_id = a.company_id
INNER JOIN company co ON co.id = a.company_id
LEFT JOIN articletext at ON at.article_id = a.id AND at.language_id = co.default_language_id
LEFT JOIN price pr ON pr.article_id = a.id AND pr.customergroup_id = co.poscustomergroup_id AND pr.minmultiplier = 1
LEFT JOIN tax ptax ON ptax.id = pr.tax_id
LEFT JOIN tax ctax ON ctax.id = co.taxrate
WHERE EXISTS (
SELECT 1
FROM categoryarticle ca
INNER JOIN posterminalcategory pc ON pc.category_id = ca.category_id
WHERE ca.article_id = a.id
AND pc.posterminal_id = @posterminalId
)
AND CAST(strftime('%s', a.updated_at) AS INTEGER) > @cursor
ORDER BY lastChanged ASC, a.id ASC
LIMIT @limit
`;

View File

@@ -0,0 +1,43 @@
import { LIST_POS_CATEGORIES as MSSQL_SQL } from '../../providers/mssql/queries/posterminal.js';
import { LIST_POS_CATEGORIES as SQLITE_SQL } from '../../providers/sqlite3/queries/posterminal.js';
import makeRunner from '../runner.js';
function normalizeCursor(cursor) {
const n = Number(cursor) || 0;
if (n <= 0) return 0;
return n >= 1e11 ? Math.floor(n / 1000) : Math.floor(n);
}
export default function getCategoryList(provider) {
const sql = provider?.dialect === 'sqlite3' ? SQLITE_SQL : MSSQL_SQL;
/**
* Lists categories for a POS terminal modified after the cursor.
*
* @param {object} input
* @param {number} input.posterminalId - The POS terminal id.
* @param {number} [input.cursor=0] - Unix timestamp cursor.
* @param {number} [input.limit=20] - Max items to return.
* @param {object} [tx] - Optional transaction.
* @returns {Promise<Array<object>>}
*/
return async ({ posterminalId, cursor = 0, limit = 20 } = {}, tx) => {
const safeLimit = Number.isInteger(limit) && limit > 0 ? limit : 20;
const result = await makeRunner(provider, tx).query(sql, {
posterminalId: Number(posterminalId),
cursor: normalizeCursor(cursor),
limit: safeLimit,
});
return result.rows.map((row) => ({
_id: String(row.id),
imghash: row.imgHash ?? null,
imgsrc: row.imgHash ?? null,
name: row.name ?? '',
pid: !row.pid || row.pid === row.rootCategoryId ? '0' : String(row.pid),
discounts: [],
sort: String(row.sort ?? 0),
lastChanged: String(row.lastChanged),
}));
};
}

View File

@@ -0,0 +1,129 @@
import {
LIST_POS_PRODUCTS as MSSQL_PRODUCTS,
LIST_POS_PRODUCT_CATEGORIES as MSSQL_CATEGORIES,
LIST_POS_CUSTOMER_GROUP_IDS as MSSQL_CG_IDS,
LIST_POS_PRODUCT_PRICES as MSSQL_PRICES,
} from '../../providers/mssql/queries/posterminal.js';
import {
LIST_POS_PRODUCTS as SQLITE_PRODUCTS,
LIST_POS_PRODUCT_CATEGORIES as SQLITE_CATEGORIES,
LIST_POS_CUSTOMER_GROUP_IDS as SQLITE_CG_IDS,
LIST_POS_PRODUCT_PRICES as SQLITE_PRICES,
} from '../../providers/sqlite3/queries/posterminal.js';
import makeRunner from '../runner.js';
function normalizeCursor(cursor) {
const n = Number(cursor) || 0;
if (n <= 0) return 0;
return n >= 1e11 ? Math.floor(n / 1000) : Math.floor(n);
}
function formatDateTime(value) {
if (!value) return '0001-01-01 00:00:00';
const date = value instanceof Date ? value : new Date(value);
if (Number.isNaN(date.getTime())) return '0001-01-01 00:00:00';
const pad = (n) => String(n).padStart(2, '0');
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
}
function grossPrice(netPrice, taxRate) {
return (Number(netPrice || 0) * (1 + Number(taxRate || 0) / 100)).toFixed(2);
}
export default function getProductList(provider) {
const isSqlite = provider?.dialect === 'sqlite3';
const productsSql = isSqlite ? SQLITE_PRODUCTS : MSSQL_PRODUCTS;
const categoriesSql = isSqlite ? SQLITE_CATEGORIES : MSSQL_CATEGORIES;
const cgIdsSql = isSqlite ? SQLITE_CG_IDS : MSSQL_CG_IDS;
const pricesSql = isSqlite ? SQLITE_PRICES : MSSQL_PRICES;
/**
* Lists products for a POS terminal modified after the cursor.
*
* @param {object} input
* @param {number} input.posterminalId - The POS terminal id.
* @param {number} [input.cursor=0] - Unix timestamp cursor.
* @param {number} [input.limit=20] - Max items to return.
* @param {object} [tx] - Optional transaction.
* @returns {Promise<Array<object>>}
*/
return async ({ posterminalId, cursor = 0, limit = 20 } = {}, tx) => {
const safeLimit = Number.isInteger(limit) && limit > 0 ? limit : 20;
const runner = makeRunner(provider, tx);
const pid = Number(posterminalId);
const [prodRes, catRes, cgRes, priceRes] = await Promise.all([
runner.query(productsSql, {
posterminalId: pid,
cursor: normalizeCursor(cursor),
limit: safeLimit,
}),
runner.query(categoriesSql, { posterminalId: pid }),
runner.query(cgIdsSql, { posterminalId: pid }),
runner.query(pricesSql, { posterminalId: pid }),
]);
const categoriesByArticle = new Map();
for (const row of catRes.rows) {
if (!categoriesByArticle.has(row.articleId)) {
categoriesByArticle.set(row.articleId, []);
}
categoriesByArticle.get(row.articleId).push(String(row.categoryId));
}
const overridesByArticle = new Map();
for (const row of priceRes.rows) {
if (!overridesByArticle.has(row.articleId)) {
overridesByArticle.set(row.articleId, new Map());
}
overridesByArticle.get(row.articleId).set(row.customerGroupId, row.netPrice);
}
const customerGroupIds = cgRes.rows.map((row) => row.id);
return prodRes.rows.map((product) => {
const overrides = overridesByArticle.get(product.id);
const basePrice = grossPrice(product.netPrice, product.taxRate);
const prices = customerGroupIds.map((customerGroupId) => {
const overrideNetPrice = overrides?.get(customerGroupId);
const price =
overrideNetPrice !== undefined
? grossPrice(overrideNetPrice, product.taxRate)
: basePrice;
return {
customerGroupId: String(customerGroupId),
customerId: '0',
price,
quantity: '0',
};
});
const categoryIds = categoriesByArticle.get(product.id) ?? [];
return {
_id: String(product.id),
imghash: product.imgHash ?? null,
imgsrc: product.imgHash ?? null,
sku: product.sku ?? '',
barcode: product.barcode ?? null,
name: product.name ?? '',
tax_rate: String(Math.round(Number(product.taxRate || 0))),
price: basePrice,
created_at: formatDateTime(product.createdAt),
lastChanged: String(product.lastChanged),
sort: String(product.sort ?? 0),
categories_id: categoryIds[0] ?? '0',
categories: categoryIds.map((categoryId) => ({ categoryId })),
prices,
is_parent: product.isParent ? '1' : '0',
parent: product.parentArticleId > 0 ? String(product.parentArticleId) : '0',
variants: product.variantName ?? '',
isCompositeProduct: product.isCompositeProduct,
attributes: [],
use_stock: product.trackStock ? '1' : '0',
quantity: String(product.fBestand ?? 0),
};
});
};
}

View File

@@ -97,19 +97,61 @@ export default function adjust(provider) {
const resolvedArticles = [];
for (const item of normalizedLines) {
const resolved = await resolveArticle(googleAuthId, item.sku, activeTx);
const artRes = await runner.query('SELECT is_father FROM article WHERE id = @articleId', {
const artRes = await runner.query('SELECT is_father, articlelist_id FROM article WHERE id = @articleId', {
articleId: resolved.articleId,
});
if (artRes.rows[0]?.is_father) {
throw new Error(`A father article cannot have stock (${item.sku})`);
}
const nameRes = await runner.query(GET_ARTICLE_NAME_SQL, {
articleId: resolved.articleId,
languageId: resolved.languageId,
});
const articleName = nameRes.rows[0]?.name ?? item.sku;
resolvedArticles.push({ ...item, ...resolved, articleName });
const articlelistId = artRes.rows[0]?.articlelist_id;
if (articlelistId != null) {
const compRes = await runner.query(`
SELECT
ala.article_id AS articleId,
ala.count,
a.sku,
at.name AS articleName
FROM articlelistarticle ala
INNER JOIN article a ON a.id = ala.article_id
LEFT JOIN articletext at ON at.article_id = a.id AND at.language_id = @languageId
WHERE ala.articlelist_id = @articlelistId
ORDER BY ala.sort ASC, ala.id ASC
`, {
articlelistId,
languageId: resolved.languageId,
});
if (compRes.rows.length === 0) {
throw new Error(`Stückliste has no items (${item.sku})`);
}
for (const comp of compRes.rows) {
const compCount = Number(comp.count || 1);
const compAmount = item.amount * compCount;
const compText = item.text
? `${item.text} (${item.sku})`
: (compAmount > 0
? `Manual stock addition (+${compAmount}) [${item.sku}]`
: `Manual stock removal (${compAmount}) [${item.sku}]`);
resolvedArticles.push({
sku: comp.sku,
warehouseId: item.warehouseId,
amount: compAmount,
text: compText,
articleId: comp.articleId,
articleName: comp.articleName || comp.sku,
});
}
} else {
const nameRes = await runner.query(GET_ARTICLE_NAME_SQL, {
articleId: resolved.articleId,
languageId: resolved.languageId,
});
const articleName = nameRes.rows[0]?.name ?? item.sku;
resolvedArticles.push({ ...item, ...resolved, articleName });
}
}
// Allocate unique adjustment number using the dedicated adjustment number scheme