u
This commit is contained in:
@@ -182,7 +182,7 @@ async function nextCustomerNumber(transaction) {
|
||||
* the search tables). Columns of the table type are named explicitly because
|
||||
* their order differs between JTL versions.
|
||||
*/
|
||||
async function createCustomer(transaction, { customerNumber, address, isPosCustomer, defaults }) {
|
||||
async function createCustomer(transaction, { customerNumber, address, defaults }) {
|
||||
const a = address || {};
|
||||
const iso = (a.countryIso || 'DE').toUpperCase();
|
||||
const kKundengruppe = Number(a.customerGroupId) || defaults.kKundengruppe;
|
||||
@@ -209,8 +209,8 @@ async function createCustomer(transaction, { customerNumber, address, isPosCusto
|
||||
.input('kSprache', sql.Int, config.kSprache)
|
||||
.input('cISO', sql.NVarChar, iso)
|
||||
.input('cBundesland', sql.NVarChar, a.state || '')
|
||||
.input('cHerkunft', sql.NVarChar, isPosCustomer ? 'Kasse' : 'POS-API')
|
||||
.input('cKassenKunde', sql.Char(1), isPosCustomer ? 'Y' : 'N')
|
||||
.input('cHerkunft', sql.NVarChar, 'Kasse')
|
||||
.input('cKassenKunde', sql.Char(1), 'Y')
|
||||
.input('nDebitorennr', sql.Int, Number(a.debtorNumber) || 0)
|
||||
.query(`
|
||||
DECLARE @returnValue INT;
|
||||
@@ -240,46 +240,55 @@ async function createCustomer(transaction, { customerNumber, address, isPosCusto
|
||||
return { kKunde, kKundengruppe };
|
||||
}
|
||||
|
||||
function isWalkInOrder(order) {
|
||||
const customerNumber = String(order.customerNumber || '').trim();
|
||||
if (!customerNumber || customerNumber === '0') return true;
|
||||
|
||||
const billing = order.billingAddress || {};
|
||||
return (billing.lastName || '') === 'Laufkunde' && !(billing.firstName || '').trim();
|
||||
}
|
||||
|
||||
async function lookupKassenkunde(transaction, defaults) {
|
||||
const result = await new sql.Request(transaction).query(
|
||||
"SELECT TOP 1 kKunde, kKundenGruppe FROM dbo.tKunde WHERE cKassenKunde = 'Y' ORDER BY kKunde"
|
||||
);
|
||||
if (result.recordset[0]) {
|
||||
return {
|
||||
kKunde: result.recordset[0].kKunde,
|
||||
kKundengruppe: result.recordset[0].kKundenGruppe || defaults.kKundengruppe,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the customer by cKundenNr; creates the Kunde on the fly when it
|
||||
* does not exist. Orders without a customer number use (or create) the
|
||||
* Kassenkunde (walk-in customer).
|
||||
* does not exist. Walk-in orders use (or create) the Kassenkunde.
|
||||
*/
|
||||
async function resolveCustomer(transaction, order, defaults) {
|
||||
const customerNumber = String(order.customerNumber || '').trim();
|
||||
|
||||
if (customerNumber) {
|
||||
const result = await new sql.Request(transaction)
|
||||
.input('cKundenNr', sql.NVarChar, customerNumber)
|
||||
.query('SELECT TOP 1 kKunde, kKundenGruppe FROM dbo.tKunde WHERE cKundenNr = @cKundenNr');
|
||||
if (result.recordset[0]) {
|
||||
return {
|
||||
kKunde: result.recordset[0].kKunde,
|
||||
kKundengruppe: result.recordset[0].kKundenGruppe || defaults.kKundengruppe,
|
||||
};
|
||||
}
|
||||
if (isWalkInOrder(order)) {
|
||||
const kassenKunde = await lookupKassenkunde(transaction, defaults);
|
||||
if (kassenKunde) return kassenKunde;
|
||||
return createCustomer(transaction, {
|
||||
customerNumber,
|
||||
customerNumber: await nextCustomerNumber(transaction),
|
||||
address: order.billingAddress,
|
||||
isPosCustomer: false,
|
||||
defaults,
|
||||
});
|
||||
}
|
||||
|
||||
const kassenKunde = await new sql.Request(transaction).query(
|
||||
"SELECT TOP 1 kKunde, kKundenGruppe FROM dbo.tKunde WHERE cKassenKunde = 'Y' ORDER BY kKunde"
|
||||
);
|
||||
if (kassenKunde.recordset[0]) {
|
||||
const customerNumber = String(order.customerNumber || '').trim();
|
||||
const result = await new sql.Request(transaction)
|
||||
.input('cKundenNr', sql.NVarChar, customerNumber)
|
||||
.query('SELECT TOP 1 kKunde, kKundenGruppe FROM dbo.tKunde WHERE cKundenNr = @cKundenNr');
|
||||
if (result.recordset[0]) {
|
||||
return {
|
||||
kKunde: kassenKunde.recordset[0].kKunde,
|
||||
kKundengruppe: kassenKunde.recordset[0].kKundenGruppe || defaults.kKundengruppe,
|
||||
kKunde: result.recordset[0].kKunde,
|
||||
kKundengruppe: result.recordset[0].kKundenGruppe || defaults.kKundengruppe,
|
||||
};
|
||||
}
|
||||
|
||||
return createCustomer(transaction, {
|
||||
customerNumber: await nextCustomerNumber(transaction),
|
||||
customerNumber,
|
||||
address: order.billingAddress,
|
||||
isPosCustomer: true,
|
||||
defaults,
|
||||
});
|
||||
}
|
||||
@@ -364,11 +373,41 @@ async function insertOrderItem(transaction, kAuftrag, item) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Links a JTL-Wawi order (and its positions) back to the originating
|
||||
* POS order/position ids via Pos.tAuftragMapping / Pos.tAuftragPositionMapping,
|
||||
* so the POS system can recognise orders it already pushed into JTL-Wawi.
|
||||
* JTL PosOrderCreationService.CheckIfOrderExists — skip re-import when the POS
|
||||
* order id is already mapped to a Wawi order for this subshop.
|
||||
*/
|
||||
async function insertPosOrderMapping(transaction, kAuftrag, kPosAuftrag) {
|
||||
async function findExistingPosOrderMapping(kPosAuftrag) {
|
||||
const kShopSubShop = getActiveShopSubshopId();
|
||||
if (!Number.isInteger(kPosAuftrag) || !kShopSubShop) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const result = await getPool()
|
||||
.request()
|
||||
.input('kPosAuftrag', sql.Int, kPosAuftrag)
|
||||
.input('kShopSubShop', sql.Int, kShopSubShop)
|
||||
.query(`
|
||||
SELECT TOP 1 m.kAuftrag, a.cAuftragsNr
|
||||
FROM Pos.tAuftragMapping m
|
||||
LEFT JOIN Verkauf.tAuftrag a ON a.kAuftrag = m.kAuftrag
|
||||
WHERE m.kPosAuftrag = @kPosAuftrag
|
||||
AND m.kShopSubShop = @kShopSubShop
|
||||
AND m.kAuftrag IS NOT NULL
|
||||
ORDER BY m.kAuftrag DESC
|
||||
`);
|
||||
|
||||
const row = result.recordset[0];
|
||||
if (!row?.kAuftrag) {
|
||||
return null;
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Links a JTL-Wawi order back to the originating POS order id.
|
||||
* MERGE matches JTL CreateOrUpdate outcome for the final committed row.
|
||||
*/
|
||||
async function upsertPosOrderMapping(transaction, kAuftrag, kPosAuftrag) {
|
||||
const kShopSubShop = getActiveShopSubshopId();
|
||||
if (!Number.isInteger(kPosAuftrag) || !kShopSubShop) {
|
||||
return;
|
||||
@@ -378,8 +417,15 @@ async function insertPosOrderMapping(transaction, kAuftrag, kPosAuftrag) {
|
||||
.input('kPosAuftrag', sql.Int, kPosAuftrag)
|
||||
.input('kShopSubShop', sql.Int, kShopSubShop)
|
||||
.query(`
|
||||
INSERT INTO Pos.tAuftragMapping (kAuftrag, kPosAuftrag, kShopSubShop)
|
||||
VALUES (@kAuftrag, @kPosAuftrag, @kShopSubShop)
|
||||
MERGE INTO Pos.tAuftragMapping WITH (HOLDLOCK) AS Target
|
||||
USING (SELECT @kAuftrag AS kAuftrag, @kPosAuftrag AS kPosAuftrag, @kShopSubShop AS kShopSubShop) AS Source
|
||||
ON Target.kPosAuftrag = Source.kPosAuftrag
|
||||
AND Target.kShopSubShop = Source.kShopSubShop
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET Target.kAuftrag = Source.kAuftrag
|
||||
WHEN NOT MATCHED BY TARGET THEN
|
||||
INSERT (kAuftrag, kPosAuftrag, kShopSubShop)
|
||||
VALUES (Source.kAuftrag, Source.kPosAuftrag, Source.kShopSubShop);
|
||||
`);
|
||||
}
|
||||
|
||||
@@ -441,6 +487,17 @@ async function insertPayment(transaction, kAuftrag, payment, order, orderDate, z
|
||||
}
|
||||
|
||||
export async function createOrder(order) {
|
||||
const kPosAuftrag = Number.parseInt(order.externalId, 10);
|
||||
if (Number.isInteger(kPosAuftrag) && kPosAuftrag > 0) {
|
||||
const existing = await findExistingPosOrderMapping(kPosAuftrag);
|
||||
if (existing) {
|
||||
return {
|
||||
orderId: String(existing.kAuftrag),
|
||||
orderNumber: existing.cAuftragsNr || '',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const defaults = await getDefaults();
|
||||
const transaction = new sql.Transaction(getPool());
|
||||
await transaction.begin(sql.ISOLATION_LEVEL.READ_COMMITTED);
|
||||
@@ -490,7 +547,7 @@ export async function createOrder(order) {
|
||||
const kAuftrag = resultAuftrag.recordset[0].kAuftrag;
|
||||
|
||||
const kPosAuftrag = Number.parseInt(order.externalId, 10);
|
||||
await insertPosOrderMapping(transaction, kAuftrag, kPosAuftrag);
|
||||
await upsertPosOrderMapping(transaction, kAuftrag, kPosAuftrag);
|
||||
|
||||
await insertOrderAddress(transaction, kAuftrag, kKunde, order.shippingAddress, 0);
|
||||
await insertOrderAddress(transaction, kAuftrag, kKunde, order.billingAddress, 1);
|
||||
|
||||
23
src/queries/max-order-id.js
Normal file
23
src/queries/max-order-id.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import sql from 'mssql';
|
||||
import { getPool } from '../db.js';
|
||||
import { getActiveShopSubshopId } from '../shop.js';
|
||||
|
||||
const MAX_ORDER_ID_SQL = `
|
||||
SELECT ISNULL(MAX(kPosAuftrag), 0) AS MaxOrderId
|
||||
FROM Pos.tAuftragMapping
|
||||
WHERE kShopSubShop = @kShopSubShop;
|
||||
`;
|
||||
|
||||
export async function getMaxOrderIdCount() {
|
||||
const kShopSubShop = getActiveShopSubshopId();
|
||||
if (!kShopSubShop) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const result = await getPool()
|
||||
.request()
|
||||
.input('kShopSubShop', sql.Int, kShopSubShop)
|
||||
.query(MAX_ORDER_ID_SQL);
|
||||
|
||||
return result.recordset[0]?.MaxOrderId ?? 0;
|
||||
}
|
||||
Reference in New Issue
Block a user