composite & deleted

This commit is contained in:
seb
2026-07-12 20:39:01 +02:00
parent 3ca6e894ab
commit ca190b2832
17 changed files with 571 additions and 11 deletions

View File

@@ -1,4 +1,4 @@
import { createImageHandler } from './image-handler.js';
import { createImageHandler } from '../image-handler.js';
export const method = 'GET';
export const path = '/v1/cimage';

View File

@@ -0,0 +1,14 @@
import { sendJson } from '../http.js';
import { getDeletedEntityList } from '../queries/deleted-entity-list.js';
export const method = 'GET';
export const path = '/v1/deletedentity';
export async function handle(_req, res, { url }) {
const cursor = Number(url.searchParams.get('lastChangedDeletedEntity')) || 0;
const limit = Number(url.searchParams.get('limit')) || 600;
const deletedEntities = await getDeletedEntityList({ cursor, limit });
return sendJson(res, 200, deletedEntities);
}

View File

@@ -2,9 +2,11 @@ import * as category from './category.js';
import * as cimage from './cimage.js';
import * as client from './client.js';
import * as customergroup from './customergroup.js';
import * as deletedEntity from './deleted-entity.js';
import * as init from './init.js';
import * as order from './order.js';
import * as pimage from './pimage.js';
import * as product from './product.js';
import * as productcomposite from './productcomposite.js';
export const endpoints = [client, init, category, product, pimage, cimage, customergroup, order];
export const endpoints = [client, init, category, product, productcomposite, deletedEntity, pimage, cimage, customergroup, order];

View File

@@ -1,7 +1,9 @@
import { sendJson } from '../http.js';
import { getMaxExternalId } from '../order-log.js';
import { getCategoryCount } from '../queries/category-count.js';
import { getCompositeProductCount } from '../queries/composite-product-count.js';
import { getCustomerGroupCount } from '../queries/customer-groups.js';
import { getDeletedEntityCount } from '../queries/deleted-entity-count.js';
import { getProductCount } from '../queries/product-count.js';
export const method = 'GET';
@@ -11,11 +13,15 @@ export async function handle(_req, res, { url }) {
const productCursor = Number(url.searchParams.get('lastChangedProduct')) || 0;
const categoryCursor = Number(url.searchParams.get('lastChangedCategory')) || 0;
const customerGroupCursor = Number(url.searchParams.get('lastChangedCustomerGroup')) || 0;
const compositeProductCursor = Number(url.searchParams.get('lastChangedCompositeProduct')) || 0;
const deletedEntityCursor = Number(url.searchParams.get('lastChangedDeletedEntity')) || 0;
const [productCount, categoryCount, customerGroupCount] = await Promise.all([
const [productCount, categoryCount, customerGroupCount, compositeProductCount, deletedEntityCount] = await Promise.all([
getProductCount({ cursor: productCursor }),
getCategoryCount({ cursor: categoryCursor }),
getCustomerGroupCount({ cursor: customerGroupCursor }),
getCompositeProductCount({ cursor: compositeProductCursor }),
getDeletedEntityCount({ cursor: deletedEntityCursor }),
]);
return sendJson(res, 200, {
@@ -24,10 +30,10 @@ export async function handle(_req, res, { url }) {
category_count: String(categoryCount),
customer_count: '0',
customerGroup_count: String(customerGroupCount),
compositeProduct_count: '0',
compositeProduct_count: String(compositeProductCount),
configurationGroup_count: '0',
configurationItem_count: '0',
deletedEntity_count: '0',
deletedEntity_count: String(deletedEntityCount),
max_orderId_count: getMaxExternalId(),
});
}

View File

@@ -1,4 +1,4 @@
import { createImageHandler } from './image-handler.js';
import { createImageHandler } from '../image-handler.js';
export const method = 'GET';
export const path = '/v1/pimage';

View File

@@ -0,0 +1,14 @@
import { sendJson } from '../http.js';
import { getCompositeProductList } from '../queries/composite-product-list.js';
export const method = 'GET';
export const path = '/v1/productcomposite';
export async function handle(_req, res, { url }) {
const cursor = Number(url.searchParams.get('lastChangedCompositeProduct')) || 0;
const limit = Number(url.searchParams.get('limit')) || 100;
const composites = await getCompositeProductList({ cursor, limit });
return sendJson(res, 200, composites);
}

View File

@@ -1,6 +1,6 @@
import { sendBinary, sendJson } from '../http.js';
import { resizeImage } from '../image-resize.js';
import { getImageByHash } from '../queries/image.js';
import { sendBinary, sendJson } from './http.js';
import { resizeImage } from './image-resize.js';
import { getImageByHash } from './queries/image.js';
export function createImageHandler() {
return async function handle(_req, res, { url }) {

View File

@@ -0,0 +1,18 @@
import sql from 'mssql';
import { getPool } from '../db.js';
const COMPOSITE_PRODUCT_COUNT_SQL = `
SELECT COUNT(DISTINCT a.kArtikel) AS CompositeProductCount
FROM dbo.tArtikel a
INNER JOIN dbo.tStueckliste s ON s.kStueckliste = a.kStueckliste
WHERE a.kStueckliste <> 0
AND CONVERT(BIGINT, a.bRowversion) > @cursor;
`;
export async function getCompositeProductCount({ cursor = 0 } = {}) {
const result = await getPool()
.request()
.input('cursor', sql.BigInt, cursor)
.query(COMPOSITE_PRODUCT_COUNT_SQL);
return result.recordset[0]?.CompositeProductCount ?? 0;
}

View File

@@ -0,0 +1,30 @@
import sql from 'mssql';
import { getPool } from '../db.js';
const COMPOSITE_PRODUCT_LIST_SQL = `
SELECT TOP (@limit)
s.kVaterArtikel AS productId,
s.kArtikel AS productIdComponent,
CONVERT(VARCHAR(20), s.fAnzahl, 2) AS quantity,
CONVERT(BIGINT, a.bRowversion) AS lastChanged
FROM dbo.tStueckliste s
INNER JOIN dbo.tArtikel a ON a.kArtikel = s.kVaterArtikel
WHERE a.kStueckliste <> 0
AND CONVERT(BIGINT, a.bRowversion) > @cursor
ORDER BY lastChanged ASC;
`;
export async function getCompositeProductList({ cursor = 0, limit = 100 } = {}) {
const result = await getPool()
.request()
.input('cursor', sql.BigInt, cursor)
.input('limit', sql.Int, limit)
.query(COMPOSITE_PRODUCT_LIST_SQL);
return result.recordset.map((row) => ({
productId: String(row.productId),
productIdComponent: String(row.productIdComponent),
quantity: row.quantity,
lastChanged: String(row.lastChanged),
}));
}

View File

@@ -0,0 +1,16 @@
import sql from 'mssql';
import { getPool } from '../db.js';
const DELETED_ENTITY_COUNT_SQL = `
SELECT COUNT(*) AS DeletedEntityCount
FROM Pos.vDeletedEntity
WHERE CONVERT(BIGINT, vDeletedEntity.bLastChanged) > @cursor;
`;
export async function getDeletedEntityCount({ cursor = 0 } = {}) {
const result = await getPool()
.request()
.input('cursor', sql.BigInt, cursor)
.query(DELETED_ENTITY_COUNT_SQL);
return result.recordset[0]?.DeletedEntityCount ?? 0;
}

View File

@@ -0,0 +1,26 @@
import sql from 'mssql';
import { getPool } from '../db.js';
const DELETED_ENTITY_LIST_SQL = `
SELECT TOP (@limit)
vDeletedEntity.kEntityId,
vDeletedEntity.nEntityType,
CONVERT(BIGINT, vDeletedEntity.bLastChanged) AS lastChanged
FROM Pos.vDeletedEntity
WHERE CONVERT(BIGINT, vDeletedEntity.bLastChanged) > @cursor
ORDER BY lastChanged ASC;
`;
export async function getDeletedEntityList({ cursor = 0, limit = 600 } = {}) {
const result = await getPool()
.request()
.input('cursor', sql.BigInt, cursor)
.input('limit', sql.Int, limit)
.query(DELETED_ENTITY_LIST_SQL);
return result.recordset.map((row) => ({
entityId: String(row.kEntityId),
entityType: String(row.nEntityType),
lastChanged: String(row.lastChanged),
}));
}

View File

@@ -38,6 +38,7 @@ SELECT TOP (@limit)
) AS categoryIds,
a.nIstVater AS isParent,
a.kVaterArtikel AS parentArticleId,
CASE WHEN a.kStueckliste <> 0 THEN '1' ELSE '0' END AS isCompositeProduct,
(
SELECT TOP 1 pv.cVariantName
FROM Pos.vProductVariant pv
@@ -144,6 +145,7 @@ export async function getProductList({ cursor = 0, limit = 20 } = {}) {
is_parent: product.isParent ? '1' : '0',
parent: product.parentArticleId > 0 ? String(product.parentArticleId) : '0',
variants: product.variantName ?? '',
isCompositeProduct: product.isCompositeProduct,
attributes: articleAttributes?.attributes ?? [],
...(articleAttributes?.deposit ?? {}),
};