u
This commit is contained in:
2
API.md
2
API.md
@@ -296,7 +296,7 @@ No `limit` — returns all groups newer than the cursor.
|
||||
|
||||
### `GET /v1/customer`
|
||||
|
||||
Customer deltas. Only customers of customer group `1` are returned, scoped to the active shop (`vCustomer.kShop = active kShop`) and its subshop (`tInetKundeShop.kSubShop = active kShopSubshop`). Rows are ordered by `bLastChanged` ascending, and only rows with `bLastChanged <= nMaxLastChanged` of the active subshop are returned.
|
||||
Customer deltas. Customers are scoped to the active shop (`vCustomer.kShop = active kShop`) and its subshop (`tInetKundeShop.kSubShop = active kShopSubshop`). Rows are ordered by `bLastChanged` ascending, and only rows with `bLastChanged > lastChangedCustomer` are returned (matching the count semantics of `/v1/init`).
|
||||
|
||||
| Param | Default |
|
||||
|---|---|
|
||||
|
||||
@@ -9,8 +9,7 @@ void handle_customer(HttpRequest& req, HttpResponse& resp, RouteContext& /*ctx*/
|
||||
int limit = req.get_query_int("limit", 20);
|
||||
int shop = get_active_shop_id();
|
||||
int subshop = get_active_shop_subshop_id();
|
||||
int64_t max_last_changed = get_active_max_last_changed();
|
||||
|
||||
auto customers = get_customer_list(cursor, limit, shop, subshop, max_last_changed);
|
||||
auto customers = get_customer_list(cursor, limit, shop, subshop);
|
||||
resp.send_json(200, customers);
|
||||
}
|
||||
|
||||
@@ -43,8 +43,7 @@ static const char* CUSTOMER_COUNT_SQL =
|
||||
"SELECT COUNT(DISTINCT k.kKunde) AS cnt FROM dbo.tkunde k "
|
||||
"LEFT JOIN dbo.tInetKundeShop iks ON iks.kKunde = k.kKunde "
|
||||
"AND iks.kShop = ? AND iks.kSubShop = ? "
|
||||
"WHERE k.kKundenGruppe = 1 "
|
||||
"AND (? = 0 OR EXISTS (SELECT 1 FROM dbo.tInetKundeShop x "
|
||||
"WHERE (? = 0 OR EXISTS (SELECT 1 FROM dbo.tInetKundeShop x "
|
||||
"WHERE x.kKunde = k.kKunde AND x.kShop = ?)) "
|
||||
"AND CONVERT(BIGINT, k.bRowversion) > ?";
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
#include "../db/pool.hpp"
|
||||
#include "../log.hpp"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "shop.hpp"
|
||||
#include <cstdio>
|
||||
|
||||
static const char* CUSTOMER_LIST_SQL =
|
||||
"SELECT TOP (?) "
|
||||
@@ -37,8 +39,6 @@ static const char* CUSTOMER_LIST_SQL =
|
||||
" AND tInetKundeShop.kSubShop = ? "
|
||||
"WHERE vCustomer.kShop = ? "
|
||||
" AND CONVERT(BIGINT, vCustomer.bLastChanged) > ? "
|
||||
" AND CONVERT(BIGINT, vCustomer.bLastChanged) <= ? "
|
||||
" AND vCustomer.kCustomerGroupId = 1 "
|
||||
"ORDER BY vCustomer.bLastChanged ASC";
|
||||
|
||||
inline std::string format_birthday(const std::string& c_date_of_birth) {
|
||||
@@ -56,13 +56,12 @@ inline std::string format_birthday(const std::string& c_date_of_birth) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
inline nlohmann::json get_customer_list(int64_t cursor, int limit, int shop, int subshop, int64_t max_last_changed) {
|
||||
inline nlohmann::json get_customer_list(int64_t cursor, int limit, int shop, int subshop) {
|
||||
std::vector<Param> ps = {
|
||||
{ParamType::Int, "", limit},
|
||||
{ParamType::Int, "", subshop},
|
||||
{ParamType::Int, "", shop},
|
||||
{ParamType::BigInt, "", cursor},
|
||||
{ParamType::BigInt, "", max_last_changed},
|
||||
};
|
||||
ResultSet rs;
|
||||
if (!get_pool().execute(CUSTOMER_LIST_SQL, ps, rs)) {
|
||||
|
||||
@@ -11,8 +11,7 @@ LEFT JOIN dbo.tInetKundeShop iks
|
||||
ON iks.kKunde = k.kKunde
|
||||
AND iks.kShop = @kShop
|
||||
AND iks.kSubShop = @SubShopId
|
||||
WHERE k.kKundenGruppe = 1
|
||||
AND (@kShop = 0 OR EXISTS (
|
||||
WHERE (@kShop = 0 OR EXISTS (
|
||||
SELECT 1 FROM dbo.tInetKundeShop x
|
||||
WHERE x.kKunde = k.kKunde AND x.kShop = @kShop
|
||||
))
|
||||
|
||||
@@ -2,7 +2,7 @@ import sql from 'mssql';
|
||||
import { isDemoMode } from '../demo/mode.js';
|
||||
import { getDemoCustomerList } from '../demo/store.js';
|
||||
import { getPool } from '../db.js';
|
||||
import { getActiveMaxLastChanged, getActiveShopId, getActiveShopSubshopId } from '../shop.js';
|
||||
import { getActiveShopId, getActiveShopSubshopId } from '../shop.js';
|
||||
|
||||
const CUSTOMER_LIST_SQL = `
|
||||
SELECT TOP (@Limit)
|
||||
@@ -38,8 +38,6 @@ LEFT JOIN dbo.tInetKundeShop
|
||||
AND tInetKundeShop.kSubShop = @SubShopId
|
||||
WHERE vCustomer.kShop = @ShopId
|
||||
AND CONVERT(BIGINT, vCustomer.bLastChanged) > @bLastChanged
|
||||
AND CONVERT(BIGINT, vCustomer.bLastChanged) <= @MaxLastChanged
|
||||
AND vCustomer.kCustomerGroupId = 1
|
||||
ORDER BY vCustomer.bLastChanged ASC;
|
||||
`;
|
||||
|
||||
@@ -72,7 +70,6 @@ export async function getCustomerList({ cursor = 0, limit = 20 } = {}) {
|
||||
.input('ShopId', sql.Int, getActiveShopId())
|
||||
.input('SubShopId', sql.Int, getActiveShopSubshopId())
|
||||
.input('bLastChanged', sql.BigInt, cursor)
|
||||
.input('MaxLastChanged', sql.BigInt, getActiveMaxLastChanged())
|
||||
.query(CUSTOMER_LIST_SQL);
|
||||
|
||||
return result.recordset.map((row) => ({
|
||||
|
||||
@@ -82,6 +82,26 @@ function priceOverridesSql(articleIds) {
|
||||
`;
|
||||
}
|
||||
|
||||
// Returns the first image hash (ordered by nNr) per article, matching the main
|
||||
// query's imgHash selection. Used to resolve the parent's image for products
|
||||
// without a picture of their own.
|
||||
function imageHashesSql(articleIds) {
|
||||
const idList = articleIds.join(',');
|
||||
return `
|
||||
SELECT articleId, imgHash
|
||||
FROM (
|
||||
SELECT
|
||||
abp.kArtikel AS articleId,
|
||||
img.cHash AS imgHash,
|
||||
ROW_NUMBER() OVER (PARTITION BY abp.kArtikel ORDER BY abp.nNr) AS rn
|
||||
FROM dbo.tArtikelbildPlattform abp
|
||||
INNER JOIN dbo.tBild img ON img.kBild = abp.kBild
|
||||
WHERE abp.kArtikel IN (${idList})
|
||||
) t
|
||||
WHERE rn = 1 AND imgHash IS NOT NULL AND imgHash <> '';
|
||||
`;
|
||||
}
|
||||
|
||||
function formatDateTime(date) {
|
||||
if (!date) {
|
||||
return '0001-01-01 00:00:00';
|
||||
@@ -118,10 +138,20 @@ export async function getProductList({ cursor = 0, limit = 20 } = {}) {
|
||||
|
||||
const overridesByArticle = new Map();
|
||||
let attributesByArticle = new Map();
|
||||
let parentImageHashes = new Map();
|
||||
if (articleIds.length > 0) {
|
||||
const [overrideResult, attributeMap] = await Promise.all([
|
||||
// Include any parent articles referenced by products in this batch so we
|
||||
// can resolve the parent image for products without a picture of their own,
|
||||
// even when the parent itself is not part of the batch.
|
||||
const parentIds = products
|
||||
.map((p) => (p.parentArticleId > 0 && !p.imgHash ? Number(p.parentArticleId) : null))
|
||||
.filter((id) => id !== null && !articleIds.includes(id));
|
||||
const hashIds = [...new Set([...articleIds, ...parentIds])];
|
||||
|
||||
const [overrideResult, attributeMap, imageHashResult] = await Promise.all([
|
||||
pool.request().query(priceOverridesSql(articleIds)),
|
||||
getProductAttributes(pool, articleIds),
|
||||
pool.request().query(imageHashesSql(hashIds)),
|
||||
]);
|
||||
attributesByArticle = attributeMap;
|
||||
for (const row of overrideResult.recordset) {
|
||||
@@ -130,6 +160,9 @@ export async function getProductList({ cursor = 0, limit = 20 } = {}) {
|
||||
}
|
||||
overridesByArticle.get(row.articleId).set(row.customerGroupId, row.netPrice);
|
||||
}
|
||||
for (const row of imageHashResult.recordset) {
|
||||
parentImageHashes.set(row.articleId, row.imgHash);
|
||||
}
|
||||
}
|
||||
|
||||
return products.map((product) => {
|
||||
@@ -151,10 +184,16 @@ export async function getProductList({ cursor = 0, limit = 20 } = {}) {
|
||||
const categoryIds = product.categoryIds ? product.categoryIds.split(',') : [];
|
||||
const articleAttributes = attributesByArticle.get(product.id);
|
||||
|
||||
// If a product has a parent and no picture of its own, fall back to the
|
||||
// parent's image hash.
|
||||
const hasOwnImage = !!product.imgHash;
|
||||
const parentHash = !hasOwnImage ? parentImageHashes.get(product.parentArticleId) : null;
|
||||
const imageHash = hasOwnImage ? product.imgHash : parentHash;
|
||||
|
||||
return {
|
||||
_id: String(product.id),
|
||||
imghash: product.imgHash ?? null,
|
||||
imgsrc: product.imgHash ?? null,
|
||||
imghash: imageHash ?? null,
|
||||
imgsrc: imageHash ?? null,
|
||||
sku: product.sku,
|
||||
barcode: product.barcode ?? null,
|
||||
name: product.name,
|
||||
|
||||
Reference in New Issue
Block a user