u
This commit is contained in:
179
scratch/test-deposit-options.js
Normal file
179
scratch/test-deposit-options.js
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { mkdtempSync, rmSync } from 'node:fs';
|
||||||
|
import { tmpdir } from 'node:os';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import LibDashStore from '../src/index.js';
|
||||||
|
import { BetterSqlite3Provider } from '../src/providers/sqlite3/index.js';
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
console.log('--- Testing deposit via mandated optionslist ---');
|
||||||
|
|
||||||
|
const tempDir = mkdtempSync(join(tmpdir(), 'test-deposit-'));
|
||||||
|
const dbFile = join(tempDir, 'test.db');
|
||||||
|
const storageDir = join(tempDir, 'storage');
|
||||||
|
|
||||||
|
const provider = new BetterSqlite3Provider({
|
||||||
|
filename: dbFile,
|
||||||
|
storageDir,
|
||||||
|
});
|
||||||
|
|
||||||
|
await provider.initialize();
|
||||||
|
const store = await new LibDashStore(provider);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const googleAuthId = await store.google_auth.add('tester@example.com');
|
||||||
|
const company = await store.company.create({
|
||||||
|
name: 'Deposit Corp',
|
||||||
|
iso639: 'de',
|
||||||
|
taxrate: 19,
|
||||||
|
reducedtaxrate: 7,
|
||||||
|
googleAuthId,
|
||||||
|
});
|
||||||
|
await store.google_auth.setSelectedCompany(googleAuthId, company.id);
|
||||||
|
|
||||||
|
// 1. Create a category
|
||||||
|
const cat = await store.category.createUnderParent({
|
||||||
|
googleAuthId,
|
||||||
|
parentId: company.rootCategoryId,
|
||||||
|
name: 'Getränke',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Create terminal and enable category
|
||||||
|
const terminal = await store.posterminal.upsert(googleAuthId, {
|
||||||
|
name: 'POS 1',
|
||||||
|
pairingpin: '1234',
|
||||||
|
});
|
||||||
|
await store.posterminal.pair(terminal.id, {
|
||||||
|
token: 'pos-token-xyz',
|
||||||
|
clientserial: 'SN-001',
|
||||||
|
pairedAt: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
await store.posterminal.setOn(googleAuthId, terminal.id, cat.id);
|
||||||
|
|
||||||
|
// 3. Create beverage article
|
||||||
|
const clubMate = await store.article.createUnderParent({
|
||||||
|
googleAuthId,
|
||||||
|
parentId: cat.id,
|
||||||
|
name: 'Club Mate 0.5l',
|
||||||
|
sku: 'MATE-05',
|
||||||
|
});
|
||||||
|
await store.article.setPricesBySku(googleAuthId, clubMate.sku, [
|
||||||
|
{ net: 1.5, tax: 19 },
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 4. Create deposit article
|
||||||
|
const pfandArt = await store.article.createUnderParent({
|
||||||
|
googleAuthId,
|
||||||
|
parentId: cat.id,
|
||||||
|
name: 'Mehrwegpfand 0,08 €',
|
||||||
|
sku: 'PFAND-0.08',
|
||||||
|
track_stock: 0,
|
||||||
|
});
|
||||||
|
await store.article.setPricesBySku(googleAuthId, pfandArt.sku, [
|
||||||
|
{ gross: 0.08, tax: 19 },
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 5. Query products BEFORE linking mandated option
|
||||||
|
let prods = await store.posterminal.getProductList({ posterminalId: terminal.id });
|
||||||
|
let mateProd = prods.find((p) => p.sku === 'MATE-05');
|
||||||
|
let pfandProd = prods.find((p) => p.sku === 'PFAND-0.08');
|
||||||
|
|
||||||
|
assert.ok(mateProd, 'Club Mate should be listed');
|
||||||
|
assert.equal(mateProd.deposit, '0');
|
||||||
|
assert.equal(mateProd.d_price, '0.0');
|
||||||
|
assert.equal(mateProd.deposit_name, '');
|
||||||
|
|
||||||
|
assert.ok(pfandProd, 'Pfand article should be listed');
|
||||||
|
assert.equal(pfandProd.deposit, '0');
|
||||||
|
|
||||||
|
// 6. Link Mehrwegpfand as mandated option to Club Mate
|
||||||
|
console.log('Linking Mehrwegpfand as mandated option...');
|
||||||
|
await store.optionslist.setOptionsByArticleSku(googleAuthId, clubMate.sku, [
|
||||||
|
{
|
||||||
|
articleId: pfandArt.id,
|
||||||
|
sku: pfandArt.sku,
|
||||||
|
count: 1,
|
||||||
|
sort: 0,
|
||||||
|
mandated: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 7. Query products AFTER linking mandated option
|
||||||
|
prods = await store.posterminal.getProductList({ posterminalId: terminal.id });
|
||||||
|
mateProd = prods.find((p) => p.sku === 'MATE-05');
|
||||||
|
|
||||||
|
console.log('Club Mate with deposit option:', {
|
||||||
|
sku: mateProd.sku,
|
||||||
|
deposit: mateProd.deposit,
|
||||||
|
d_price: mateProd.d_price,
|
||||||
|
deposit_name: mateProd.deposit_name,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(mateProd.deposit, '1');
|
||||||
|
assert.equal(mateProd.d_price, '0.08');
|
||||||
|
assert.equal(mateProd.deposit_name, 'Mehrwegpfand 0,08 €');
|
||||||
|
|
||||||
|
// 8. Test option name change touching parent article
|
||||||
|
console.log('Testing option name change touching parent article...');
|
||||||
|
const mateBeforeRename = (await provider.query('SELECT updated_at FROM article WHERE id = @id', { id: clubMate.id })).rows[0].updated_at;
|
||||||
|
await new Promise((r) => setTimeout(r, 1100)); // wait >1s for timestamp delta
|
||||||
|
|
||||||
|
await store.article.updateBySku(googleAuthId, pfandArt.sku, {
|
||||||
|
name: 'Glas-Mehrwegpfand 0,08 €',
|
||||||
|
});
|
||||||
|
|
||||||
|
const mateAfterRename = (await provider.query('SELECT updated_at FROM article WHERE id = @id', { id: clubMate.id })).rows[0].updated_at;
|
||||||
|
assert.notEqual(
|
||||||
|
mateBeforeRename,
|
||||||
|
mateAfterRename,
|
||||||
|
'Club Mate updated_at must be bumped when its option article is renamed'
|
||||||
|
);
|
||||||
|
|
||||||
|
prods = await store.posterminal.getProductList({ posterminalId: terminal.id });
|
||||||
|
mateProd = prods.find((p) => p.sku === 'MATE-05');
|
||||||
|
assert.equal(mateProd.deposit_name, 'Glas-Mehrwegpfand 0,08 €');
|
||||||
|
console.log('Renamed deposit reflected on parent product:', mateProd.deposit_name);
|
||||||
|
|
||||||
|
// 9. Test option price change touching parent article
|
||||||
|
console.log('Testing option price change touching parent article...');
|
||||||
|
const mateBeforePrice = mateAfterRename;
|
||||||
|
await new Promise((r) => setTimeout(r, 1100));
|
||||||
|
|
||||||
|
await store.article.setPricesBySku(googleAuthId, pfandArt.sku, [
|
||||||
|
{ gross: 0.15, tax: 19 },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const mateAfterPrice = (await provider.query('SELECT updated_at FROM article WHERE id = @id', { id: clubMate.id })).rows[0].updated_at;
|
||||||
|
assert.notEqual(
|
||||||
|
mateBeforePrice,
|
||||||
|
mateAfterPrice,
|
||||||
|
'Club Mate updated_at must be bumped when its option article price changes'
|
||||||
|
);
|
||||||
|
|
||||||
|
prods = await store.posterminal.getProductList({ posterminalId: terminal.id });
|
||||||
|
mateProd = prods.find((p) => p.sku === 'MATE-05');
|
||||||
|
assert.equal(mateProd.d_price, '0.15');
|
||||||
|
console.log('New deposit price reflected on parent product:', mateProd.d_price);
|
||||||
|
|
||||||
|
// 10. Test clearing options
|
||||||
|
console.log('Clearing options...');
|
||||||
|
await store.optionslist.setOptionsByArticleSku(googleAuthId, clubMate.sku, []);
|
||||||
|
prods = await store.posterminal.getProductList({ posterminalId: terminal.id });
|
||||||
|
mateProd = prods.find((p) => p.sku === 'MATE-05');
|
||||||
|
|
||||||
|
assert.equal(mateProd.deposit, '0');
|
||||||
|
assert.equal(mateProd.d_price, '0.0');
|
||||||
|
assert.equal(mateProd.deposit_name, '');
|
||||||
|
|
||||||
|
console.log('✅ ALL DEPOSIT OPTIONS TESTS PASSED');
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
rmSync(tempDir, { recursive: true, force: true });
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run().catch((err) => {
|
||||||
|
console.error('Test failed:', err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -228,6 +228,16 @@ SET updated_at = SYSUTCDATETIME()
|
|||||||
WHERE id = @articleId
|
WHERE id = @articleId
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const TOUCH_OPTION_PARENT_ARTICLES_UPDATED_AT = `
|
||||||
|
UPDATE article
|
||||||
|
SET updated_at = SYSUTCDATETIME()
|
||||||
|
WHERE optionslist_id IN (
|
||||||
|
SELECT ola.optionslist_id
|
||||||
|
FROM optionslistarticle ola
|
||||||
|
WHERE ola.article_id = @articleId
|
||||||
|
)
|
||||||
|
`;
|
||||||
|
|
||||||
export const CREATE = `
|
export const CREATE = `
|
||||||
INSERT INTO article (company_id, sku, unit_id, unitamount, weight, track_stock)
|
INSERT INTO article (company_id, sku, unit_id, unitamount, weight, track_stock)
|
||||||
OUTPUT INSERTED.id
|
OUTPUT INSERTED.id
|
||||||
|
|||||||
@@ -483,6 +483,25 @@ WHERE pr.minmultiplier = 1
|
|||||||
GROUP BY pr.article_id, pr.customergroup_id
|
GROUP BY pr.article_id, pr.customergroup_id
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const LIST_POS_MANDATED_OPTIONS = `
|
||||||
|
SELECT
|
||||||
|
a.id AS articleId,
|
||||||
|
ola.count,
|
||||||
|
COALESCE(opt_at.name, opt_a.sku) AS depositName,
|
||||||
|
opt_pr.net AS depositNetPrice,
|
||||||
|
COALESCE(opt_ptax.rate, opt_ctax.rate, 0) AS depositTaxRate
|
||||||
|
FROM article a
|
||||||
|
INNER JOIN optionslistarticle ola ON ola.optionslist_id = a.optionslist_id AND ola.mandated = 1
|
||||||
|
INNER JOIN article opt_a ON opt_a.id = ola.article_id
|
||||||
|
INNER JOIN company co ON co.id = a.company_id
|
||||||
|
INNER JOIN posterminal pt ON pt.id = @posterminalId AND pt.company_id = a.company_id
|
||||||
|
LEFT JOIN articletext opt_at ON opt_at.article_id = opt_a.id AND opt_at.language_id = co.default_language_id
|
||||||
|
LEFT JOIN price opt_pr ON opt_pr.article_id = opt_a.id AND opt_pr.customergroup_id = co.poscustomergroup_id AND opt_pr.minmultiplier = 1
|
||||||
|
LEFT JOIN tax opt_ptax ON opt_ptax.id = opt_pr.tax_id
|
||||||
|
LEFT JOIN tax opt_ctax ON opt_ctax.id = co.taxrate
|
||||||
|
ORDER BY ola.sort ASC, ola.id ASC
|
||||||
|
`;
|
||||||
|
|
||||||
export const RECORD_OBITUARY = `
|
export const RECORD_OBITUARY = `
|
||||||
INSERT INTO posterminalobituary (posterminal_id, entity_type, entity_id)
|
INSERT INTO posterminalobituary (posterminal_id, entity_type, entity_id)
|
||||||
SELECT pt.id, @entityType, @entityId
|
SELECT pt.id, @entityType, @entityId
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ export const {
|
|||||||
INSERT_POSTERMINAL_OBITUARY,
|
INSERT_POSTERMINAL_OBITUARY,
|
||||||
LIST_CHILDREN_BY_PARENT_ID, LIST_PAIRED_POSTERMINAL_IDS_BY_ARTICLE,
|
LIST_CHILDREN_BY_PARENT_ID, LIST_PAIRED_POSTERMINAL_IDS_BY_ARTICLE,
|
||||||
SET_PARENT_SORT,
|
SET_PARENT_SORT,
|
||||||
SET_IS_PARENT, TOGGLE_PARENT, TOUCH_UPDATED_AT, UNLINK_CHILD, UPDATE_BRAND, UPDATE_PARENT_BY_SKU, UPDATE_TRACK_STOCK, UPDATE_UNIT,
|
SET_IS_PARENT, TOGGLE_PARENT, TOUCH_UPDATED_AT, TOUCH_OPTION_PARENT_ARTICLES_UPDATED_AT,
|
||||||
|
UNLINK_CHILD, UPDATE_BRAND, UPDATE_PARENT_BY_SKU, UPDATE_TRACK_STOCK, UPDATE_UNIT,
|
||||||
UPDATE_UNITAMOUNT, UPDATE_UNIT_AND_AMOUNT, UPDATE_WEIGHT,
|
UPDATE_UNITAMOUNT, UPDATE_UNIT_AND_AMOUNT, UPDATE_WEIGHT,
|
||||||
} = queries;
|
} = queries;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export const {
|
|||||||
FIND_BY_TOKEN, COUNT_PRODUCTS, COUNT_CATEGORIES, COUNT_CUSTOMERS,
|
FIND_BY_TOKEN, COUNT_PRODUCTS, COUNT_CATEGORIES, COUNT_CUSTOMERS,
|
||||||
COUNT_CUSTOMER_GROUPS, COUNT_COMPOSITE_PRODUCTS, MAX_ORDER_ID,
|
COUNT_CUSTOMER_GROUPS, COUNT_COMPOSITE_PRODUCTS, MAX_ORDER_ID,
|
||||||
LIST_POS_PRODUCT_CATEGORIES, LIST_POS_CUSTOMER_GROUP_IDS, LIST_POS_PRODUCT_PRICES,
|
LIST_POS_PRODUCT_CATEGORIES, LIST_POS_CUSTOMER_GROUP_IDS, LIST_POS_PRODUCT_PRICES,
|
||||||
|
LIST_POS_MANDATED_OPTIONS,
|
||||||
TOUCH_LASTSEEN, RECORD_OBITUARY, COUNT_DELETED_ENTITIES,
|
TOUCH_LASTSEEN, RECORD_OBITUARY, COUNT_DELETED_ENTITIES,
|
||||||
} = queries;
|
} = queries;
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ export default function del(provider) {
|
|||||||
|
|
||||||
await runner.query(queries.DELETE_PICS_BY_ARTICLE, { articleId: id });
|
await runner.query(queries.DELETE_PICS_BY_ARTICLE, { articleId: id });
|
||||||
|
|
||||||
|
// Touch any parent articles that used this article as an option before deleting it
|
||||||
|
await runner.query(articleQueries.TOUCH_OPTION_PARENT_ARTICLES_UPDATED_AT, { articleId: id });
|
||||||
|
|
||||||
const deleted = await runner.query(queries.DELETE_BY_ID, { articleId: id });
|
const deleted = await runner.query(queries.DELETE_BY_ID, { articleId: id });
|
||||||
if (!deleted.rowsAffected) {
|
if (!deleted.rowsAffected) {
|
||||||
throw new Error('Article not found');
|
throw new Error('Article not found');
|
||||||
|
|||||||
@@ -6,8 +6,14 @@ import {
|
|||||||
CREATE as SQLITE_CREATE_PRICE,
|
CREATE as SQLITE_CREATE_PRICE,
|
||||||
DELETE_BY_ARTICLE_AND_CUSTOMERGROUP as SQLITE_DELETE_PRICE,
|
DELETE_BY_ARTICLE_AND_CUSTOMERGROUP as SQLITE_DELETE_PRICE,
|
||||||
} from '../../providers/sqlite3/queries/price.js';
|
} from '../../providers/sqlite3/queries/price.js';
|
||||||
import { TOUCH_UPDATED_AT as MSSQL_TOUCH } from '../../providers/mssql/queries/article.js';
|
import {
|
||||||
import { TOUCH_UPDATED_AT as SQLITE_TOUCH } from '../../providers/sqlite3/queries/article.js';
|
TOUCH_UPDATED_AT as MSSQL_TOUCH,
|
||||||
|
TOUCH_OPTION_PARENT_ARTICLES_UPDATED_AT as MSSQL_TOUCH_OPTION_PARENTS,
|
||||||
|
} from '../../providers/mssql/queries/article.js';
|
||||||
|
import {
|
||||||
|
TOUCH_UPDATED_AT as SQLITE_TOUCH,
|
||||||
|
TOUCH_OPTION_PARENT_ARTICLES_UPDATED_AT as SQLITE_TOUCH_OPTION_PARENTS,
|
||||||
|
} from '../../providers/sqlite3/queries/article.js';
|
||||||
import taxEnsure from '../tax/ensure.js';
|
import taxEnsure from '../tax/ensure.js';
|
||||||
import queryBySku from './queryBySku.js';
|
import queryBySku from './queryBySku.js';
|
||||||
import resolveArticleBySku from './resolveArticleBySku.js';
|
import resolveArticleBySku from './resolveArticleBySku.js';
|
||||||
@@ -68,6 +74,7 @@ export default function setPricesBySku(provider) {
|
|||||||
const createPriceSql = isSqlite ? SQLITE_CREATE_PRICE : MSSQL_CREATE_PRICE;
|
const createPriceSql = isSqlite ? SQLITE_CREATE_PRICE : MSSQL_CREATE_PRICE;
|
||||||
const deletePriceSql = isSqlite ? SQLITE_DELETE_PRICE : MSSQL_DELETE_PRICE;
|
const deletePriceSql = isSqlite ? SQLITE_DELETE_PRICE : MSSQL_DELETE_PRICE;
|
||||||
const touchArticleSql = isSqlite ? SQLITE_TOUCH : MSSQL_TOUCH;
|
const touchArticleSql = isSqlite ? SQLITE_TOUCH : MSSQL_TOUCH;
|
||||||
|
const touchOptionParentsSql = isSqlite ? SQLITE_TOUCH_OPTION_PARENTS : MSSQL_TOUCH_OPTION_PARENTS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces the price list of an article by its SKU.
|
* Replaces the price list of an article by its SKU.
|
||||||
@@ -147,6 +154,9 @@ export default function setPricesBySku(provider) {
|
|||||||
// Touch article updated_at so POS and other consumers pick up the price change
|
// Touch article updated_at so POS and other consumers pick up the price change
|
||||||
await runner.query(touchArticleSql, { articleId });
|
await runner.query(touchArticleSql, { articleId });
|
||||||
|
|
||||||
|
// Touch any parent articles using this article as an option so their deposit price updates
|
||||||
|
await runner.query(touchOptionParentsSql, { articleId });
|
||||||
|
|
||||||
const article = await load(articleId, languageId, runner);
|
const article = await load(articleId, languageId, runner);
|
||||||
if (!article) {
|
if (!article) {
|
||||||
throw new Error('Article not found');
|
throw new Error('Article not found');
|
||||||
|
|||||||
@@ -235,6 +235,9 @@ export default function updateBySku(provider) {
|
|||||||
// Touch article updated_at so all consumers pick up changes
|
// Touch article updated_at so all consumers pick up changes
|
||||||
await runner.query(articleQueries.TOUCH_UPDATED_AT, { articleId });
|
await runner.query(articleQueries.TOUCH_UPDATED_AT, { articleId });
|
||||||
|
|
||||||
|
// Touch any parent articles that use this article as an option so their deposit name/details update on POS
|
||||||
|
await runner.query(articleQueries.TOUCH_OPTION_PARENT_ARTICLES_UPDATED_AT, { articleId });
|
||||||
|
|
||||||
const article = await load(articleId, languageId, runner);
|
const article = await load(articleId, languageId, runner);
|
||||||
if (!article) {
|
if (!article) {
|
||||||
throw new Error('Article not found');
|
throw new Error('Article not found');
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import * as mssqlQueries from '../../providers/mssql/queries/optionslist.js';
|
import * as mssqlQueries from '../../providers/mssql/queries/optionslist.js';
|
||||||
import * as sqliteQueries from '../../providers/sqlite3/queries/optionslist.js';
|
import * as sqliteQueries from '../../providers/sqlite3/queries/optionslist.js';
|
||||||
|
import { TOUCH_UPDATED_AT as MSSQL_TOUCH } from '../../providers/mssql/queries/article.js';
|
||||||
|
import { TOUCH_UPDATED_AT as SQLITE_TOUCH } from '../../providers/sqlite3/queries/article.js';
|
||||||
import resolveArticleBySku from '../article/resolveArticleBySku.js';
|
import resolveArticleBySku from '../article/resolveArticleBySku.js';
|
||||||
import getOptionsByArticleSku from './getOptionsByArticleSku.js';
|
import getOptionsByArticleSku from './getOptionsByArticleSku.js';
|
||||||
import {
|
import {
|
||||||
@@ -46,6 +48,7 @@ export default function setOptionsByArticleSku(provider) {
|
|||||||
const getOptions = getOptionsByArticleSku(provider);
|
const getOptions = getOptionsByArticleSku(provider);
|
||||||
const isSqlite = provider?.dialect === 'sqlite3';
|
const isSqlite = provider?.dialect === 'sqlite3';
|
||||||
const queries = isSqlite ? sqliteQueries : mssqlQueries;
|
const queries = isSqlite ? sqliteQueries : mssqlQueries;
|
||||||
|
const touchArticleSql = isSqlite ? SQLITE_TOUCH : MSSQL_TOUCH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets or clears the options list for an article.
|
* Sets or clears the options list for an article.
|
||||||
@@ -108,6 +111,7 @@ export default function setOptionsByArticleSku(provider) {
|
|||||||
type: 'optionslist_cleared',
|
type: 'optionslist_cleared',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
await runner.query(touchArticleSql, { articleId });
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -236,6 +240,9 @@ export default function setOptionsByArticleSku(provider) {
|
|||||||
options: result,
|
options: result,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Touch article updated_at so POS and other consumers pick up options/deposit changes
|
||||||
|
await runner.query(touchArticleSql, { articleId });
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ import {
|
|||||||
LIST_POS_PRODUCT_CATEGORIES as MSSQL_CATEGORIES,
|
LIST_POS_PRODUCT_CATEGORIES as MSSQL_CATEGORIES,
|
||||||
LIST_POS_CUSTOMER_GROUP_IDS as MSSQL_CG_IDS,
|
LIST_POS_CUSTOMER_GROUP_IDS as MSSQL_CG_IDS,
|
||||||
LIST_POS_PRODUCT_PRICES as MSSQL_PRICES,
|
LIST_POS_PRODUCT_PRICES as MSSQL_PRICES,
|
||||||
|
LIST_POS_MANDATED_OPTIONS as MSSQL_MANDATED_OPTIONS,
|
||||||
} from '../../providers/mssql/queries/posterminal.js';
|
} from '../../providers/mssql/queries/posterminal.js';
|
||||||
import {
|
import {
|
||||||
LIST_POS_PRODUCTS as SQLITE_PRODUCTS,
|
LIST_POS_PRODUCTS as SQLITE_PRODUCTS,
|
||||||
LIST_POS_PRODUCT_CATEGORIES as SQLITE_CATEGORIES,
|
LIST_POS_PRODUCT_CATEGORIES as SQLITE_CATEGORIES,
|
||||||
LIST_POS_CUSTOMER_GROUP_IDS as SQLITE_CG_IDS,
|
LIST_POS_CUSTOMER_GROUP_IDS as SQLITE_CG_IDS,
|
||||||
LIST_POS_PRODUCT_PRICES as SQLITE_PRICES,
|
LIST_POS_PRODUCT_PRICES as SQLITE_PRICES,
|
||||||
|
LIST_POS_MANDATED_OPTIONS as SQLITE_MANDATED_OPTIONS,
|
||||||
} from '../../providers/sqlite3/queries/posterminal.js';
|
} from '../../providers/sqlite3/queries/posterminal.js';
|
||||||
import makeRunner from '../runner.js';
|
import makeRunner from '../runner.js';
|
||||||
|
|
||||||
@@ -36,6 +38,7 @@ export default function getProductList(provider) {
|
|||||||
const categoriesSql = isSqlite ? SQLITE_CATEGORIES : MSSQL_CATEGORIES;
|
const categoriesSql = isSqlite ? SQLITE_CATEGORIES : MSSQL_CATEGORIES;
|
||||||
const cgIdsSql = isSqlite ? SQLITE_CG_IDS : MSSQL_CG_IDS;
|
const cgIdsSql = isSqlite ? SQLITE_CG_IDS : MSSQL_CG_IDS;
|
||||||
const pricesSql = isSqlite ? SQLITE_PRICES : MSSQL_PRICES;
|
const pricesSql = isSqlite ? SQLITE_PRICES : MSSQL_PRICES;
|
||||||
|
const mandatedOptionsSql = isSqlite ? SQLITE_MANDATED_OPTIONS : MSSQL_MANDATED_OPTIONS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists products for a POS terminal modified after the cursor.
|
* Lists products for a POS terminal modified after the cursor.
|
||||||
@@ -52,7 +55,7 @@ export default function getProductList(provider) {
|
|||||||
const runner = makeRunner(provider, tx);
|
const runner = makeRunner(provider, tx);
|
||||||
const pid = Number(posterminalId);
|
const pid = Number(posterminalId);
|
||||||
|
|
||||||
const [prodRes, catRes, cgRes, priceRes] = await Promise.all([
|
const [prodRes, catRes, cgRes, priceRes, optRes] = await Promise.all([
|
||||||
runner.query(productsSql, {
|
runner.query(productsSql, {
|
||||||
posterminalId: pid,
|
posterminalId: pid,
|
||||||
cursor: normalizeCursor(cursor),
|
cursor: normalizeCursor(cursor),
|
||||||
@@ -61,6 +64,7 @@ export default function getProductList(provider) {
|
|||||||
runner.query(categoriesSql, { posterminalId: pid }),
|
runner.query(categoriesSql, { posterminalId: pid }),
|
||||||
runner.query(cgIdsSql, { posterminalId: pid }),
|
runner.query(cgIdsSql, { posterminalId: pid }),
|
||||||
runner.query(pricesSql, { posterminalId: pid }),
|
runner.query(pricesSql, { posterminalId: pid }),
|
||||||
|
runner.query(mandatedOptionsSql, { posterminalId: pid }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const categoriesByArticle = new Map();
|
const categoriesByArticle = new Map();
|
||||||
@@ -79,6 +83,24 @@ export default function getProductList(provider) {
|
|||||||
overridesByArticle.get(row.articleId).set(row.customerGroupId, row.netPrice);
|
overridesByArticle.get(row.articleId).set(row.customerGroupId, row.netPrice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mandatedOptionsByArticle = new Map();
|
||||||
|
for (const row of optRes.rows) {
|
||||||
|
const gross =
|
||||||
|
Number(row.count || 1) *
|
||||||
|
Number(row.depositNetPrice || 0) *
|
||||||
|
(1 + Number(row.depositTaxRate || 0) / 100);
|
||||||
|
if (!mandatedOptionsByArticle.has(row.articleId)) {
|
||||||
|
mandatedOptionsByArticle.set(row.articleId, {
|
||||||
|
names: [row.depositName],
|
||||||
|
totalPrice: gross,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const existing = mandatedOptionsByArticle.get(row.articleId);
|
||||||
|
existing.names.push(row.depositName);
|
||||||
|
existing.totalPrice += gross;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const customerGroupIds = cgRes.rows.map((row) => row.id);
|
const customerGroupIds = cgRes.rows.map((row) => row.id);
|
||||||
|
|
||||||
return prodRes.rows.map((product) => {
|
return prodRes.rows.map((product) => {
|
||||||
@@ -100,6 +122,10 @@ export default function getProductList(provider) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const categoryIds = categoriesByArticle.get(product.id) ?? [];
|
const categoryIds = categoriesByArticle.get(product.id) ?? [];
|
||||||
|
const mandatedOpt = mandatedOptionsByArticle.get(product.id);
|
||||||
|
const hasDeposit = Boolean(mandatedOpt);
|
||||||
|
const depositName = mandatedOpt ? mandatedOpt.names.join(' + ') : '';
|
||||||
|
const depositPrice = mandatedOpt ? mandatedOpt.totalPrice.toFixed(2) : '0.0';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_id: String(product.id),
|
_id: String(product.id),
|
||||||
@@ -123,6 +149,9 @@ export default function getProductList(provider) {
|
|||||||
attributes: [],
|
attributes: [],
|
||||||
use_stock: product.trackStock ? '1' : '0',
|
use_stock: product.trackStock ? '1' : '0',
|
||||||
quantity: String(product.fBestand ?? 0),
|
quantity: String(product.fBestand ?? 0),
|
||||||
|
deposit: hasDeposit ? '1' : '0',
|
||||||
|
d_price: depositPrice,
|
||||||
|
deposit_name: depositName,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user