u
This commit is contained in:
@@ -32,6 +32,7 @@ CPP_SRCS := \
|
||||
src/endpoints/productcomposite.cpp \
|
||||
src/endpoints/deleted_entity.cpp \
|
||||
src/endpoints/customergroup.cpp \
|
||||
src/endpoints/customer.cpp \
|
||||
src/endpoints/order.cpp \
|
||||
src/endpoints/pimage.cpp \
|
||||
src/endpoints/cimage.cpp \
|
||||
|
||||
@@ -110,6 +110,7 @@ Place `certs/cert.pem` and `certs/key.pem` relative to the working directory whe
|
||||
| GET | `/v1/productcomposite` | Composite product sync |
|
||||
| GET | `/v1/deletedentity` | Deleted entity sync |
|
||||
| GET | `/v1/customergroup` | Customer group sync |
|
||||
| GET | `/v1/customer` | Customer sync |
|
||||
| POST | `/v1/order` | Submit orders |
|
||||
| GET | `/v1/pimage` | Product image (resized) |
|
||||
| GET | `/v1/cimage` | Category image (resized) |
|
||||
|
||||
16
jtlsrv-cpp/src/endpoints/customer.cpp
Normal file
16
jtlsrv-cpp/src/endpoints/customer.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "../http.hpp"
|
||||
#include "../tls_server.hpp"
|
||||
#include "../router.hpp"
|
||||
#include "../queries/customer_list.hpp"
|
||||
#include "../queries/shop.hpp"
|
||||
|
||||
void handle_customer(HttpRequest& req, HttpResponse& resp, RouteContext& /*ctx*/) {
|
||||
int64_t cursor = req.get_query_int64("lastChangedCustomer");
|
||||
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);
|
||||
resp.send_json(200, customers);
|
||||
}
|
||||
@@ -10,23 +10,26 @@
|
||||
void handle_init(HttpRequest& req, HttpResponse& resp, RouteContext& /*ctx*/) {
|
||||
int64_t product_cursor = req.get_query_int64("lastChangedProduct");
|
||||
int64_t category_cursor = req.get_query_int64("lastChangedCategory");
|
||||
int64_t customer_cursor = req.get_query_int64("lastChangedCustomer");
|
||||
int64_t cg_cursor = req.get_query_int64("lastChangedCustomerGroup");
|
||||
int64_t composite_cursor = req.get_query_int64("lastChangedCompositeProduct");
|
||||
int64_t deleted_cursor = req.get_query_int64("lastChangedDeletedEntity");
|
||||
|
||||
int root = config::get_int("ROOT_CATEGORY_ID", 1);
|
||||
int shop = get_active_shop_id();
|
||||
int subshop = get_active_shop_subshop_id();
|
||||
|
||||
int64_t product_count = 0, category_count = 0, cg_count = 0, composite_count = 0, deleted_count = 0;
|
||||
int64_t product_count = 0, category_count = 0, customer_count = 0, cg_count = 0, composite_count = 0, deleted_count = 0;
|
||||
int64_t max_order_id_count = 0;
|
||||
|
||||
if (get_pool().execute_scalar("SELECT 1") != 0) {
|
||||
product_count = get_product_count(root, shop, product_cursor);
|
||||
category_count = get_category_count(root, shop, category_cursor);
|
||||
customer_count = get_customer_count(shop, subshop, customer_cursor);
|
||||
cg_count = get_customer_group_count(cg_cursor);
|
||||
composite_count = get_composite_count(shop, composite_cursor);
|
||||
deleted_count = get_deleted_count(deleted_cursor);
|
||||
max_order_id_count = get_max_order_id_count(get_active_shop_subshop_id());
|
||||
max_order_id_count = get_max_order_id_count(subshop);
|
||||
if (product_cursor == 0 && category_cursor == 0 &&
|
||||
product_count == 0 && category_count == 0 && deleted_count == 0) {
|
||||
logc::warn("init: all counts zero with cursors at 0 — check DB connectivity and shop/category config");
|
||||
@@ -39,7 +42,7 @@ void handle_init(HttpRequest& req, HttpResponse& resp, RouteContext& /*ctx*/) {
|
||||
{"version", "1.10.12.0"},
|
||||
{"product_count", std::to_string(product_count)},
|
||||
{"category_count", std::to_string(category_count)},
|
||||
{"customer_count", "0"},
|
||||
{"customer_count", std::to_string(customer_count)},
|
||||
{"customerGroup_count", std::to_string(cg_count)},
|
||||
{"compositeProduct_count", std::to_string(composite_count)},
|
||||
{"configurationGroup_count", "0"},
|
||||
|
||||
@@ -107,6 +107,7 @@ extern void handle_product(HttpRequest&, HttpResponse&, RouteContext&);
|
||||
extern void handle_productcomposite(HttpRequest&, HttpResponse&, RouteContext&);
|
||||
extern void handle_deleted_entity(HttpRequest&, HttpResponse&, RouteContext&);
|
||||
extern void handle_customergroup(HttpRequest&, HttpResponse&, RouteContext&);
|
||||
extern void handle_customer(HttpRequest&, HttpResponse&, RouteContext&);
|
||||
extern void handle_order(HttpRequest&, HttpResponse&, RouteContext&);
|
||||
extern void handle_pimage(HttpRequest&, HttpResponse&, RouteContext&);
|
||||
extern void handle_cimage(HttpRequest&, HttpResponse&, RouteContext&);
|
||||
@@ -188,6 +189,7 @@ int main(int /*argc*/, char* argv[]) {
|
||||
router.add_route("GET", "/v1/productcomposite", handle_productcomposite);
|
||||
router.add_route("GET", "/v1/deletedentity", handle_deleted_entity);
|
||||
router.add_route("GET", "/v1/customergroup", handle_customergroup);
|
||||
router.add_route("GET", "/v1/customer", handle_customer);
|
||||
router.add_route("POST", "/v1/order", handle_order);
|
||||
router.add_route("GET", "/v1/pimage", handle_pimage);
|
||||
router.add_route("GET", "/v1/cimage", handle_cimage);
|
||||
|
||||
@@ -39,6 +39,15 @@ static const char* COMPOSITE_PRODUCT_COUNT_SQL =
|
||||
"AND ks.kShop = ? WHERE ka.kArtikel = a.kArtikel)) "
|
||||
"AND CONVERT(BIGINT, a.bRowversion) > ?";
|
||||
|
||||
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 x.kKunde = k.kKunde AND x.kShop = ?)) "
|
||||
"AND CONVERT(BIGINT, k.bRowversion) > ?";
|
||||
|
||||
static const char* DELETED_ENTITY_COUNT_SQL =
|
||||
"SELECT COUNT(*) AS cnt FROM Pos.vDeletedEntity "
|
||||
"WHERE CONVERT(BIGINT, vDeletedEntity.bLastChanged) > ?";
|
||||
@@ -63,6 +72,12 @@ inline int64_t get_composite_count(int k_shop, int64_t cursor) {
|
||||
{{ParamType::Int,"",k_shop},{ParamType::Int,"",k_shop},
|
||||
{ParamType::BigInt,"",cursor}});
|
||||
}
|
||||
inline int64_t get_customer_count(int k_shop, int k_subshop, int64_t cursor) {
|
||||
return get_pool().execute_scalar(CUSTOMER_COUNT_SQL,
|
||||
{{ParamType::Int,"",k_shop},{ParamType::Int,"",k_subshop},
|
||||
{ParamType::Int,"",k_shop},{ParamType::Int,"",k_shop},
|
||||
{ParamType::BigInt,"",cursor}});
|
||||
}
|
||||
inline int64_t get_deleted_count(int64_t cursor) {
|
||||
return get_pool().execute_scalar(DELETED_ENTITY_COUNT_SQL,
|
||||
{{ParamType::BigInt,"",cursor}});
|
||||
|
||||
101
jtlsrv-cpp/src/queries/customer_list.hpp
Normal file
101
jtlsrv-cpp/src/queries/customer_list.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
#include "../db/pool.hpp"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "shop.hpp"
|
||||
|
||||
static const char* CUSTOMER_LIST_SQL =
|
||||
"SELECT TOP (?) "
|
||||
"vCustomer.kId, "
|
||||
"ISNULL(tInetKundeShop.cShopKundenNr, vCustomer.cCustomerNumber) AS cCustomerNumber, "
|
||||
"vCustomer.cFirstName, "
|
||||
"vCustomer.cLastName, "
|
||||
"vCustomer.cTitle, "
|
||||
"vCustomer.cCompany, "
|
||||
"vCustomer.cAddress, "
|
||||
"vCustomer.cAddressSupplement, "
|
||||
"vCustomer.cCity, "
|
||||
"vCustomer.cPostalCode, "
|
||||
"vCustomer.cState, "
|
||||
"vCustomer.cCountry, "
|
||||
"vCustomer.cPhone, "
|
||||
"vCustomer.cEmailAddress, "
|
||||
"ISNULL(tInetKundeShop.kKundenGruppe, vCustomer.kCustomerGroupId) AS kCustomerGroupId, "
|
||||
"vCustomer.cSalutation, "
|
||||
"vCustomer.cDateOfBirth, "
|
||||
"vCustomer.fDiscount, "
|
||||
"vCustomer.cFederalTaxId, "
|
||||
"CONVERT(BIGINT, vCustomer.bLastChanged) AS lastChanged, "
|
||||
"vCustomer.kShop, "
|
||||
"vCustomer.dLastModified, "
|
||||
"vCustomer.dActive, "
|
||||
"vCustomer.dInactive, "
|
||||
"vCustomer.nDebtorNumber "
|
||||
"FROM Pos.vCustomer "
|
||||
"LEFT JOIN dbo.tInetKundeShop "
|
||||
" ON vCustomer.kId = tInetKundeShop.kKunde "
|
||||
" AND tInetKundeShop.kShop = vCustomer.kShop "
|
||||
" 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) {
|
||||
if (c_date_of_birth.empty()) return "";
|
||||
// cDateOfBirth is expected as "dd.MM.yyyy"
|
||||
int d = 0, m = 0, y = 0;
|
||||
if (std::sscanf(c_date_of_birth.c_str(), "%d.%d.%d", &d, &m, &y) != 3) {
|
||||
return "";
|
||||
}
|
||||
if (y < 1900 || y > 2100 || m < 1 || m > 12 || d < 1 || d > 31) {
|
||||
return "";
|
||||
}
|
||||
char buf[32];
|
||||
std::snprintf(buf, sizeof(buf), "%04d-%02d-%02d 00:00:00", y, m, d);
|
||||
return buf;
|
||||
}
|
||||
|
||||
inline nlohmann::json get_customer_list(int64_t cursor, int limit, int shop, int subshop, int64_t max_last_changed) {
|
||||
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)) {
|
||||
logc::warn("customer list query failed (cursor=%lld limit=%d shop=%d subshop=%d)",
|
||||
(long long)cursor, limit, shop, subshop);
|
||||
return nlohmann::json::array();
|
||||
}
|
||||
|
||||
nlohmann::json result = nlohmann::json::array();
|
||||
for (auto& row : rs) {
|
||||
result.push_back({
|
||||
{"id", row[0].str},
|
||||
{"customerNumber", row[1].type == CellType::Null ? "" : row[1].str},
|
||||
{"firstname", row[2].type == CellType::Null ? "" : row[2].str},
|
||||
{"lastname", row[3].type == CellType::Null ? "" : row[3].str},
|
||||
{"title", row[4].type == CellType::Null ? "" : row[4].str},
|
||||
{"company", row[5].type == CellType::Null ? "" : row[5].str},
|
||||
{"address", row[6].type == CellType::Null ? "" : row[6].str},
|
||||
{"addressSupplement", row[7].type == CellType::Null ? "" : row[7].str},
|
||||
{"city", row[8].type == CellType::Null ? "" : row[8].str},
|
||||
{"postalCode", row[9].type == CellType::Null ? "" : row[9].str},
|
||||
{"state", row[10].type == CellType::Null ? "" : row[10].str},
|
||||
{"country", row[11].type == CellType::Null ? "" : row[11].str},
|
||||
{"phone", row[12].type == CellType::Null ? "" : row[12].str},
|
||||
{"email", row[13].type == CellType::Null ? "" : row[13].str},
|
||||
{"customerGroupId", row[14].type == CellType::Null ? "1" : row[14].str},
|
||||
{"salutation", row[15].type == CellType::Null ? "" : row[15].str},
|
||||
{"birthday", format_birthday(row[16].str)},
|
||||
{"discount", row[17].type == CellType::Null ? "0.00" : row[17].str},
|
||||
{"taxIdNumber", row[18].type == CellType::Null ? "" : row[18].str},
|
||||
{"lastChanged", row[19].str},
|
||||
{"debtorNumber", row[24].type == CellType::Null ? "0" : row[24].str},
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
#include "shop.hpp"
|
||||
#include "../log.hpp"
|
||||
#include "../http.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
int g_active_shop_id = 0;
|
||||
int g_active_shop_subshop_id = 0;
|
||||
int64_t g_active_max_last_changed = 0;
|
||||
|
||||
static int cell_to_int(const Cell& cell) {
|
||||
if (cell.type == CellType::Int64) return static_cast<int>(cell.i64);
|
||||
@@ -11,10 +13,16 @@ static int cell_to_int(const Cell& cell) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int64_t cell_to_int64(const Cell& cell) {
|
||||
if (cell.type == CellType::Int64) return cell.i64;
|
||||
if (cell.type == CellType::String && !cell.str.empty()) return parse_int64(cell.str, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool fetch_active_shop() {
|
||||
ResultSet rs;
|
||||
if (!get_pool().execute(
|
||||
"SELECT TOP 1 kShop, kShopSubshop FROM dbo.tShopSubshop "
|
||||
"SELECT TOP 1 kShop, kShopSubshop, nMaxLastChanged FROM dbo.tShopSubshop "
|
||||
"WHERE nGesperrt = 0 ORDER BY kShop",
|
||||
rs) || rs.empty()) {
|
||||
logc::warn("failed to load active shop from dbo.tShopSubshop");
|
||||
@@ -23,6 +31,9 @@ bool fetch_active_shop() {
|
||||
|
||||
g_active_shop_id = cell_to_int(rs[0][0]);
|
||||
g_active_shop_subshop_id = rs[0].size() > 1 ? cell_to_int(rs[0][1]) : 0;
|
||||
logc::info("Active shop: kShop=%d kShopSubshop=%d", g_active_shop_id, g_active_shop_subshop_id);
|
||||
g_active_max_last_changed = rs[0].size() > 2 ? cell_to_int64(rs[0][2]) : 0;
|
||||
logc::info("Active shop: kShop=%d kShopSubshop=%d nMaxLastChanged=%lld",
|
||||
g_active_shop_id, g_active_shop_subshop_id,
|
||||
(long long)g_active_max_last_changed);
|
||||
return g_active_shop_id > 0;
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
|
||||
extern int g_active_shop_id;
|
||||
extern int g_active_shop_subshop_id;
|
||||
extern int64_t g_active_max_last_changed;
|
||||
|
||||
inline int get_active_shop_id() { return g_active_shop_id; }
|
||||
inline int get_active_shop_subshop_id() { return g_active_shop_subshop_id; }
|
||||
inline int64_t get_active_max_last_changed() { return g_active_max_last_changed; }
|
||||
|
||||
bool fetch_active_shop();
|
||||
|
||||
Reference in New Issue
Block a user