u
This commit is contained in:
43
src/store/posterminal/getCategoryList.js
Normal file
43
src/store/posterminal/getCategoryList.js
Normal 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),
|
||||
}));
|
||||
};
|
||||
}
|
||||
129
src/store/posterminal/getProductList.js
Normal file
129
src/store/posterminal/getProductList.js
Normal 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),
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user