u
This commit is contained in:
266
src/store/articlewarehousesummary/syncSummary.js
Normal file
266
src/store/articlewarehousesummary/syncSummary.js
Normal file
@@ -0,0 +1,266 @@
|
||||
/**
|
||||
* Centralized sync and rebuild logic for the materialized `articlewarehousesummary` table.
|
||||
*
|
||||
* Keeps `articlewarehousesummary` in sync with ledger movements, track_stock status,
|
||||
* and warehouses.
|
||||
*/
|
||||
|
||||
const SQL_FIND_ARTICLES_BY_MANIFESTARTICLE = `
|
||||
SELECT a.id AS articleId, a.company_id AS companyId, a.track_stock AS trackStock
|
||||
FROM manifestarticle ma
|
||||
INNER JOIN manifest m ON m.id = ma.manifest_id
|
||||
INNER JOIN article a ON a.company_id = m.company_id
|
||||
AND (
|
||||
a.sku = ma.sku
|
||||
OR EXISTS (SELECT 1 FROM articlesupplier asup WHERE asup.article_id = a.id AND asup.sku = ma.sku)
|
||||
)
|
||||
WHERE ma.id = @manifestarticleId AND m.company_id = @companyId;
|
||||
`;
|
||||
|
||||
const SQL_GET_STOCK_FROM_LEDGER = `
|
||||
SELECT COALESCE(SUM(l.amount), 0) 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 l.warehouse_id = @warehouseId
|
||||
AND m.company_id = @companyId
|
||||
AND (
|
||||
ma.sku = (SELECT sku FROM article WHERE id = @articleId)
|
||||
OR ma.sku IN (SELECT asup.sku FROM articlesupplier asup WHERE asup.article_id = @articleId)
|
||||
);
|
||||
`;
|
||||
|
||||
const SQL_UPSERT_SUMMARY_SQLITE = `
|
||||
INSERT INTO articlewarehousesummary (article_id, warehouse_id, stock)
|
||||
VALUES (@articleId, @warehouseId, @stock)
|
||||
ON CONFLICT (article_id, warehouse_id) DO UPDATE SET stock = excluded.stock;
|
||||
`;
|
||||
|
||||
const SQL_UPSERT_SUMMARY_MSSQL = `
|
||||
MERGE INTO articlewarehousesummary AS target
|
||||
USING (SELECT @articleId AS article_id, @warehouseId AS warehouse_id) AS source
|
||||
ON (target.article_id = source.article_id AND target.warehouse_id = source.warehouse_id)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET stock = @stock
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (article_id, warehouse_id, stock)
|
||||
VALUES (@articleId, @warehouseId, @stock);
|
||||
`;
|
||||
|
||||
const SQL_INIT_FOR_ARTICLE_SQLITE = `
|
||||
INSERT INTO articlewarehousesummary (article_id, warehouse_id, stock)
|
||||
SELECT @articleId, w.id, 0
|
||||
FROM warehouse w
|
||||
WHERE w.company_id = @companyId
|
||||
ON CONFLICT (article_id, warehouse_id) DO UPDATE SET stock = 0;
|
||||
`;
|
||||
|
||||
const SQL_INIT_FOR_ARTICLE_MSSQL = `
|
||||
MERGE INTO articlewarehousesummary AS target
|
||||
USING (
|
||||
SELECT @articleId AS article_id, w.id AS warehouse_id
|
||||
FROM warehouse w
|
||||
WHERE w.company_id = @companyId
|
||||
) AS source
|
||||
ON (target.article_id = source.article_id AND target.warehouse_id = source.warehouse_id)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET stock = 0
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (article_id, warehouse_id, stock)
|
||||
VALUES (source.article_id, source.warehouse_id, 0);
|
||||
`;
|
||||
|
||||
const SQL_INIT_FOR_NEW_WAREHOUSE_SQLITE = `
|
||||
INSERT INTO articlewarehousesummary (article_id, warehouse_id, stock)
|
||||
SELECT a.id, @warehouseId, 0
|
||||
FROM article a
|
||||
WHERE a.company_id = @companyId AND a.track_stock = 1
|
||||
ON CONFLICT (article_id, warehouse_id) DO UPDATE SET stock = 0;
|
||||
`;
|
||||
|
||||
const SQL_INIT_FOR_NEW_WAREHOUSE_MSSQL = `
|
||||
MERGE INTO articlewarehousesummary AS target
|
||||
USING (
|
||||
SELECT a.id AS article_id, @warehouseId AS warehouse_id
|
||||
FROM article a
|
||||
WHERE a.company_id = @companyId AND a.track_stock = 1
|
||||
) AS source
|
||||
ON (target.article_id = source.article_id AND target.warehouse_id = source.warehouse_id)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET stock = 0
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (article_id, warehouse_id, stock)
|
||||
VALUES (source.article_id, source.warehouse_id, 0);
|
||||
`;
|
||||
|
||||
const SQL_DELETE_BY_ARTICLE = `
|
||||
DELETE FROM articlewarehousesummary WHERE article_id = @articleId;
|
||||
`;
|
||||
|
||||
/**
|
||||
* Updates `articlewarehousesummary` after a ledger entry is appended with a warehouseId.
|
||||
*
|
||||
* @param {object} runner - Database runner.
|
||||
* @param {object} opts
|
||||
* @param {number} opts.companyId
|
||||
* @param {number} opts.manifestarticleId
|
||||
* @param {number|null} opts.warehouseId
|
||||
*/
|
||||
export async function syncArticleWarehouseSummaryForLedger(runner, { companyId, manifestarticleId, warehouseId }) {
|
||||
if (warehouseId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isSqlite = runner.dialect === 'sqlite3';
|
||||
const upsertSql = isSqlite ? SQL_UPSERT_SUMMARY_SQLITE : SQL_UPSERT_SUMMARY_MSSQL;
|
||||
|
||||
const res = await runner.query(SQL_FIND_ARTICLES_BY_MANIFESTARTICLE, {
|
||||
manifestarticleId,
|
||||
companyId,
|
||||
});
|
||||
|
||||
const rows = res.rows ?? [];
|
||||
for (const row of rows) {
|
||||
const articleId = row.articleId;
|
||||
const isTracked = Boolean(row.trackStock ?? 0);
|
||||
|
||||
if (!isTracked) {
|
||||
await runner.query(SQL_DELETE_BY_ARTICLE, { articleId });
|
||||
continue;
|
||||
}
|
||||
|
||||
const stockRes = await runner.query(SQL_GET_STOCK_FROM_LEDGER, {
|
||||
articleId,
|
||||
warehouseId,
|
||||
companyId,
|
||||
});
|
||||
const rawStock = stockRes.rows[0]?.stock ?? 0;
|
||||
const stock = Math.round(Number(rawStock) * 1000) / 1000;
|
||||
|
||||
await runner.query(upsertSql, {
|
||||
articleId,
|
||||
warehouseId,
|
||||
stock,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes `articlewarehousesummary` with stock = 0 for all warehouses of the company
|
||||
* when track_stock is enabled for an article.
|
||||
*
|
||||
* @param {object} runner - Database runner.
|
||||
* @param {object} opts
|
||||
* @param {number} opts.companyId
|
||||
* @param {number} opts.articleId
|
||||
*/
|
||||
export async function initArticleWarehouseSummary(runner, { companyId, articleId }) {
|
||||
const isSqlite = runner.dialect === 'sqlite3';
|
||||
const sql = isSqlite ? SQL_INIT_FOR_ARTICLE_SQLITE : SQL_INIT_FOR_ARTICLE_MSSQL;
|
||||
await runner.query(sql, { companyId, articleId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all rows for an article from `articlewarehousesummary` when track_stock is disabled.
|
||||
*
|
||||
* @param {object} runner - Database runner.
|
||||
* @param {object} opts
|
||||
* @param {number} opts.articleId
|
||||
*/
|
||||
export async function removeArticleWarehouseSummary(runner, { articleId }) {
|
||||
await runner.query(SQL_DELETE_BY_ARTICLE, { articleId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes entries in `articlewarehousesummary` for a newly created warehouse
|
||||
* for all articles with stock tracking enabled.
|
||||
*
|
||||
* @param {object} runner - Database runner.
|
||||
* @param {object} opts
|
||||
* @param {number} opts.companyId
|
||||
* @param {number} opts.warehouseId
|
||||
*/
|
||||
export async function initWarehouseSummaryForNewWarehouse(runner, { companyId, warehouseId }) {
|
||||
const isSqlite = runner.dialect === 'sqlite3';
|
||||
const sql = isSqlite ? SQL_INIT_FOR_NEW_WAREHOUSE_SQLITE : SQL_INIT_FOR_NEW_WAREHOUSE_MSSQL;
|
||||
await runner.query(sql, { companyId, warehouseId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds the entire `articlewarehousesummary` for a given company.
|
||||
*
|
||||
* @param {object} runner - Database runner.
|
||||
* @param {number} companyId - The company id.
|
||||
*/
|
||||
export async function rebuildArticleWarehouseSummaries(runner, companyId) {
|
||||
const isSqlite = runner.dialect === 'sqlite3';
|
||||
|
||||
if (isSqlite) {
|
||||
const sqlRebuildSqlite = `
|
||||
DELETE FROM articlewarehousesummary
|
||||
WHERE article_id IN (SELECT id FROM article WHERE company_id = @companyId);
|
||||
|
||||
INSERT INTO articlewarehousesummary (article_id, warehouse_id, stock)
|
||||
SELECT
|
||||
a.id AS article_id,
|
||||
w.id AS warehouse_id,
|
||||
COALESCE(s.stock, 0) AS stock
|
||||
FROM article a
|
||||
CROSS JOIN warehouse w ON w.company_id = a.company_id
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
ca.id AS article_id,
|
||||
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
|
||||
INNER JOIN article ca ON ca.company_id = m.company_id
|
||||
AND (
|
||||
ca.sku = ma.sku
|
||||
OR EXISTS (SELECT 1 FROM articlesupplier asup WHERE asup.article_id = ca.id AND asup.sku = ma.sku)
|
||||
)
|
||||
WHERE m.company_id = @companyId
|
||||
AND l.warehouse_id IS NOT NULL
|
||||
GROUP BY ca.id, l.warehouse_id
|
||||
) s ON s.article_id = a.id AND s.warehouse_id = w.id
|
||||
WHERE a.company_id = @companyId
|
||||
AND a.track_stock = 1;
|
||||
`;
|
||||
await runner.query(sqlRebuildSqlite, { companyId });
|
||||
} else {
|
||||
const sqlRebuildMssql = `
|
||||
DELETE FROM articlewarehousesummary
|
||||
WHERE article_id IN (SELECT id FROM article WHERE company_id = @companyId);
|
||||
|
||||
INSERT INTO articlewarehousesummary (article_id, warehouse_id, stock)
|
||||
SELECT
|
||||
a.id AS article_id,
|
||||
w.id AS warehouse_id,
|
||||
COALESCE(s.stock, 0) AS stock
|
||||
FROM article a
|
||||
CROSS JOIN warehouse w
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
ca.id AS article_id,
|
||||
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
|
||||
INNER JOIN article ca ON ca.company_id = m.company_id
|
||||
AND (
|
||||
ca.sku = ma.sku
|
||||
OR EXISTS (SELECT 1 FROM articlesupplier asup WHERE asup.article_id = ca.id AND asup.sku = ma.sku)
|
||||
)
|
||||
WHERE m.company_id = @companyId
|
||||
AND l.warehouse_id IS NOT NULL
|
||||
GROUP BY ca.id, l.warehouse_id
|
||||
) s ON s.article_id = a.id AND s.warehouse_id = w.id
|
||||
WHERE a.company_id = @companyId
|
||||
AND w.company_id = @companyId
|
||||
AND a.track_stock = 1;
|
||||
`;
|
||||
await runner.query(sqlRebuildMssql, { companyId });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user