u
This commit is contained in:
@@ -4,7 +4,7 @@ Protected. Requires a valid session cookie (`credentials: 'include'`).
|
|||||||
|
|
||||||
JTL-POS order upload
|
JTL-POS order upload
|
||||||
|
|
||||||
Imports orders from JTL-POS, creating order manifests, payments, and shipments with stock deductions.
|
Imports orders from JTL-POS, creating order and retoure manifests, payments, and shipments with stock adjustments.
|
||||||
|
|
||||||
## Query parameters
|
## Query parameters
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ async function resolveGoogleAuthId(provider, store, companyId) {
|
|||||||
* /v1/order:
|
* /v1/order:
|
||||||
* post:
|
* post:
|
||||||
* summary: JTL-POS order upload
|
* summary: JTL-POS order upload
|
||||||
* description: Imports orders from JTL-POS, creating order manifests, payments, and shipments with stock deductions.
|
* description: Imports orders from JTL-POS, creating order and retoure manifests, payments, and shipments with stock adjustments.
|
||||||
* tags:
|
* tags:
|
||||||
* - POS Sync
|
* - POS Sync
|
||||||
* security:
|
* security:
|
||||||
@@ -124,12 +124,12 @@ export async function handleOrder(req, res, next) {
|
|||||||
// 1. Deduplication check
|
// 1. Deduplication check
|
||||||
if (extCheck) {
|
if (extCheck) {
|
||||||
const existing = await provider.query(
|
const existing = await provider.query(
|
||||||
`SELECT id, number, external_number FROM manifest WHERE company_id = @companyId AND (external_number = @extCheck OR number = @extCheck) AND type = 'order'`,
|
`SELECT id, number, external_number, type FROM manifest WHERE company_id = @companyId AND (external_number = @extCheck OR number = @extCheck) AND type IN ('order', 'retoure')`,
|
||||||
{ companyId, extCheck }
|
{ companyId, extCheck }
|
||||||
);
|
);
|
||||||
if (existing.rows.length > 0) {
|
if (existing.rows.length > 0) {
|
||||||
const row = existing.rows[0];
|
const row = existing.rows[0];
|
||||||
const msg = `order already mapped, skipped save (kAuftrag=${row.id}, ${row.number})`;
|
const msg = `${row.type || 'document'} already mapped, skipped save (id=${row.id}, ${row.number})`;
|
||||||
log.info(`order externalId=${externalOrderId} skipped: ${msg}`);
|
log.info(`order externalId=${externalOrderId} skipped: ${msg}`);
|
||||||
results.push({
|
results.push({
|
||||||
status: 'OK',
|
status: 'OK',
|
||||||
@@ -183,16 +183,30 @@ export async function handleOrder(req, res, next) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Lines
|
// 3. Lines - separate sales (positive) and returns (negative)
|
||||||
const rawItems = Array.isArray(order.orderItems) ? order.orderItems : [];
|
const rawItems = Array.isArray(order.orderItems) ? order.orderItems : [];
|
||||||
const lines = rawItems.map((it) => {
|
const positiveLines = [];
|
||||||
|
const retoureLines = [];
|
||||||
|
|
||||||
|
for (const it of rawItems) {
|
||||||
const sku = it.sku ? String(it.sku).trim() : null;
|
const sku = it.sku ? String(it.sku).trim() : null;
|
||||||
const name = it.name ? String(it.name).trim() : (sku ? `Artikel ${sku}` : 'Artikel');
|
const name = it.name ? String(it.name).trim() : (sku ? `Artikel ${sku}` : 'Artikel');
|
||||||
let amount = Number(it.quantity) || 1;
|
const rawQuantity = Number(it.quantity) || 1;
|
||||||
if (String(it.isReturn) === '1' && amount > 0) {
|
const rawPriceNet = Number(it.priceNet) || 0;
|
||||||
amount = -amount;
|
const rawPriceGross = Number(it.priceGross) || 0;
|
||||||
}
|
const rawTotalPriceNet = Number(it.totalPriceNet) || 0;
|
||||||
const net = Number(it.priceNet) || 0;
|
const rawTotalPriceGross = Number(it.totalPriceGross) || 0;
|
||||||
|
|
||||||
|
const isReturn = String(it.isReturn) === '1' ||
|
||||||
|
it.isReturn === true ||
|
||||||
|
String(it.isReturn).toLowerCase() === 'true' ||
|
||||||
|
String(order.type) === '4' ||
|
||||||
|
rawQuantity < 0 ||
|
||||||
|
rawPriceNet < 0 ||
|
||||||
|
rawPriceGross < 0 ||
|
||||||
|
rawTotalPriceNet < 0 ||
|
||||||
|
rawTotalPriceGross < 0;
|
||||||
|
|
||||||
const taxrate = Number(it.vat) || 19;
|
const taxrate = Number(it.vat) || 19;
|
||||||
const rebate = Number(it.discountPercent) || 0;
|
const rebate = Number(it.discountPercent) || 0;
|
||||||
const comment = it.note ? String(it.note).trim() : null;
|
const comment = it.note ? String(it.note).trim() : null;
|
||||||
@@ -200,58 +214,120 @@ export async function handleOrder(req, res, next) {
|
|||||||
if (/^(summe glätten|shippingcost|versandkosten)$/i.test(name)) {
|
if (/^(summe glätten|shippingcost|versandkosten)$/i.test(name)) {
|
||||||
shippable = false;
|
shippable = false;
|
||||||
}
|
}
|
||||||
return {
|
|
||||||
sku,
|
if (isReturn) {
|
||||||
name,
|
const amount = Math.abs(rawQuantity);
|
||||||
amount,
|
const netMag = Math.abs(rawPriceNet) || (Math.abs(rawPriceGross) ? Math.abs(rawPriceGross) / (1 + taxrate / 100) : 0);
|
||||||
net,
|
const net = -netMag;
|
||||||
taxrate,
|
retoureLines.push({
|
||||||
rebate,
|
sku,
|
||||||
comment,
|
name,
|
||||||
shippable,
|
amount,
|
||||||
};
|
net,
|
||||||
});
|
taxrate,
|
||||||
|
rebate,
|
||||||
|
comment,
|
||||||
|
shippable,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const amount = Math.abs(rawQuantity);
|
||||||
|
const net = Math.abs(rawPriceNet) || (Math.abs(rawPriceGross) ? Math.abs(rawPriceGross) / (1 + taxrate / 100) : 0);
|
||||||
|
positiveLines.push({
|
||||||
|
sku,
|
||||||
|
name,
|
||||||
|
amount,
|
||||||
|
net,
|
||||||
|
taxrate,
|
||||||
|
rebate,
|
||||||
|
comment,
|
||||||
|
shippable,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 4. Date and Numbers
|
// 4. Date and Numbers
|
||||||
const parsedDate = order.creationDate ? new Date(order.creationDate) : new Date();
|
const parsedDate = order.creationDate ? new Date(order.creationDate) : new Date();
|
||||||
const validDate = !Number.isNaN(parsedDate.getTime()) ? parsedDate.toISOString() : new Date().toISOString();
|
const validDate = !Number.isNaN(parsedDate.getTime()) ? parsedDate.toISOString() : new Date().toISOString();
|
||||||
const orderNumber = await allocateOrderNumber(provider, companyId);
|
|
||||||
const externalNumber = externalOrderNumber || (externalOrderId ? String(externalOrderId) : null);
|
const externalNumber = externalOrderNumber || (externalOrderId ? String(externalOrderId) : null);
|
||||||
|
|
||||||
// 5. Create Order
|
// 5. Create Order Manifest for positive positions
|
||||||
let createdOrder = null;
|
let createdOrder = null;
|
||||||
try {
|
if (positiveLines.length > 0) {
|
||||||
createdOrder = await store.order.create(googleAuthId, {
|
const orderNumber = await allocateOrderNumber(provider, companyId);
|
||||||
number: orderNumber,
|
try {
|
||||||
externalNumber,
|
|
||||||
date: validDate,
|
|
||||||
createdAt: validDate,
|
|
||||||
entityId: entityId || undefined,
|
|
||||||
lines,
|
|
||||||
});
|
|
||||||
} catch (createErr) {
|
|
||||||
const parentMatch = createErr.message?.match(/A parent article cannot be booked on documents \(([^)]+)\)/);
|
|
||||||
if (parentMatch) {
|
|
||||||
const offendingSku = parentMatch[1];
|
|
||||||
for (const l of lines) {
|
|
||||||
if (l.sku === offendingSku) l.sku = null;
|
|
||||||
}
|
|
||||||
createdOrder = await store.order.create(googleAuthId, {
|
createdOrder = await store.order.create(googleAuthId, {
|
||||||
number: orderNumber,
|
number: orderNumber,
|
||||||
externalNumber,
|
externalNumber,
|
||||||
date: validDate,
|
date: validDate,
|
||||||
createdAt: validDate,
|
createdAt: validDate,
|
||||||
entityId: entityId || undefined,
|
entityId: entityId || undefined,
|
||||||
lines,
|
lines: positiveLines,
|
||||||
});
|
});
|
||||||
} else {
|
} catch (createErr) {
|
||||||
throw createErr;
|
const parentMatch = createErr.message?.match(/A parent article cannot be booked on documents \(([^)]+)\)/);
|
||||||
|
if (parentMatch) {
|
||||||
|
const offendingSku = parentMatch[1];
|
||||||
|
for (const l of positiveLines) {
|
||||||
|
if (l.sku === offendingSku) l.sku = null;
|
||||||
|
}
|
||||||
|
createdOrder = await store.order.create(googleAuthId, {
|
||||||
|
number: orderNumber,
|
||||||
|
externalNumber,
|
||||||
|
date: validDate,
|
||||||
|
createdAt: validDate,
|
||||||
|
entityId: entityId || undefined,
|
||||||
|
lines: positiveLines,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw createErr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 5b. Create Retoure Manifest for negative positions
|
||||||
|
let createdRetoure = null;
|
||||||
|
if (retoureLines.length > 0) {
|
||||||
|
try {
|
||||||
|
createdRetoure = await store.retoure.create(googleAuthId, {
|
||||||
|
externalNumber,
|
||||||
|
date: validDate,
|
||||||
|
createdAt: validDate,
|
||||||
|
entityId: entityId || undefined,
|
||||||
|
lines: retoureLines,
|
||||||
|
});
|
||||||
|
} catch (createErr) {
|
||||||
|
const parentMatch = createErr.message?.match(/A parent article cannot be booked on documents \(([^)]+)\)/);
|
||||||
|
if (parentMatch) {
|
||||||
|
const offendingSku = parentMatch[1];
|
||||||
|
for (const l of retoureLines) {
|
||||||
|
if (l.sku === offendingSku) l.sku = null;
|
||||||
|
}
|
||||||
|
createdRetoure = await store.retoure.create(googleAuthId, {
|
||||||
|
externalNumber,
|
||||||
|
date: validDate,
|
||||||
|
createdAt: validDate,
|
||||||
|
entityId: entityId || undefined,
|
||||||
|
lines: retoureLines,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw createErr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!createdOrder && !createdRetoure) {
|
||||||
|
log.info(`order externalId=${externalOrderId} had no lines, skipping`);
|
||||||
|
results.push({
|
||||||
|
status: 'OK',
|
||||||
|
externalOrderId,
|
||||||
|
message: '',
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// 6. Payments
|
// 6. Payments
|
||||||
const payments = Array.isArray(order.payments) ? order.payments : [];
|
const payments = Array.isArray(order.payments) ? order.payments : [];
|
||||||
if (payments.length > 0) {
|
if (payments.length > 0 && (createdOrder || createdRetoure)) {
|
||||||
const accounts = await store.account.list(googleAuthId).catch(() => []);
|
const accounts = await store.account.list(googleAuthId).catch(() => []);
|
||||||
const accountMap = new Map();
|
const accountMap = new Map();
|
||||||
for (const acc of accounts) {
|
for (const acc of accounts) {
|
||||||
@@ -282,27 +358,76 @@ export async function handleOrder(req, res, next) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const p of payments) {
|
if (createdRetoure && !createdOrder) {
|
||||||
const amount = Number(p.amount) || 0;
|
for (const p of payments) {
|
||||||
if (amount <= 0) continue;
|
const raw = Number(p.amount) || 0;
|
||||||
const type = p.paymentMethodName || 'CASH';
|
if (raw === 0) continue;
|
||||||
const accountId = await resolveAccountId(type);
|
const amount = -Math.abs(raw);
|
||||||
if (accountId) {
|
const type = p.paymentMethodName || 'CASH';
|
||||||
|
const accountId = await resolveAccountId(type);
|
||||||
|
if (accountId) {
|
||||||
|
await store.payment.create(googleAuthId, {
|
||||||
|
accountId,
|
||||||
|
manifestId: createdRetoure.id,
|
||||||
|
type,
|
||||||
|
amount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (createdOrder && !createdRetoure) {
|
||||||
|
for (const p of payments) {
|
||||||
|
const amount = Number(p.amount) || 0;
|
||||||
|
if (amount <= 0) continue;
|
||||||
|
const type = p.paymentMethodName || 'CASH';
|
||||||
|
const accountId = await resolveAccountId(type);
|
||||||
|
if (accountId) {
|
||||||
|
await store.payment.create(googleAuthId, {
|
||||||
|
accountId,
|
||||||
|
manifestId: createdOrder.id,
|
||||||
|
type,
|
||||||
|
amount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (createdOrder && createdRetoure) {
|
||||||
|
const retoureTotalGross = retoureLines.reduce((sum, l) => {
|
||||||
|
const lineGross = (l.amount || 0) * (l.net || 0) * (1 - (l.rebate || 0) / 100) * (1 + (l.taxrate || 0) / 100);
|
||||||
|
return sum + lineGross;
|
||||||
|
}, 0);
|
||||||
|
const retourePaymentType = payments[0]?.paymentMethodName || 'CASH';
|
||||||
|
const retoureAccId = await resolveAccountId(retourePaymentType);
|
||||||
|
if (retoureAccId && retoureTotalGross !== 0) {
|
||||||
await store.payment.create(googleAuthId, {
|
await store.payment.create(googleAuthId, {
|
||||||
accountId,
|
accountId: retoureAccId,
|
||||||
manifestId: createdOrder.id,
|
manifestId: createdRetoure.id,
|
||||||
type,
|
type: retourePaymentType,
|
||||||
amount,
|
amount: Math.round(retoureTotalGross * 100) / 100,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
for (const p of payments) {
|
||||||
|
const amount = Number(p.amount) || 0;
|
||||||
|
if (amount <= 0) continue;
|
||||||
|
const type = p.paymentMethodName || 'CASH';
|
||||||
|
const accountId = await resolveAccountId(type);
|
||||||
|
if (accountId) {
|
||||||
|
await store.payment.create(googleAuthId, {
|
||||||
|
accountId,
|
||||||
|
manifestId: createdOrder.id,
|
||||||
|
type,
|
||||||
|
amount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. Deliver / Shipment
|
// 7. Deliver / Shipment for Order (positive lines)
|
||||||
const shouldDeliver = String(order.settings?.deliver ?? '').trim() === '1' || order.deliver === true || order.shipped === true;
|
const shouldDeliver = String(order.settings?.deliver ?? '').trim() === '1' || order.deliver === true || order.shipped === true;
|
||||||
if (shouldDeliver) {
|
let defaultWarehouseId = null;
|
||||||
|
const resolveWarehouse = async () => {
|
||||||
|
if (defaultWarehouseId) return defaultWarehouseId;
|
||||||
let warehouses = await store.warehouse.list(googleAuthId).catch(() => []);
|
let warehouses = await store.warehouse.list(googleAuthId).catch(() => []);
|
||||||
let defaultWarehouseId = warehouses[0]?.id;
|
defaultWarehouseId = warehouses[0]?.id;
|
||||||
if (!defaultWarehouseId) {
|
if (!defaultWarehouseId) {
|
||||||
try {
|
try {
|
||||||
const createdWh = await store.warehouse.create(googleAuthId, { name: 'Standardlager' });
|
const createdWh = await store.warehouse.create(googleAuthId, { name: 'Standardlager' });
|
||||||
@@ -313,11 +438,16 @@ export async function handleOrder(req, res, next) {
|
|||||||
defaultWarehouseId = warehouses[0]?.id;
|
defaultWarehouseId = warehouses[0]?.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!defaultWarehouseId) {
|
return defaultWarehouseId;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (createdOrder && shouldDeliver) {
|
||||||
|
const whId = await resolveWarehouse();
|
||||||
|
if (!whId) {
|
||||||
throw new Error('Warehouse not found');
|
throw new Error('Warehouse not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const shippableLines = (createdOrder.lines || lines)
|
const shippableLines = (createdOrder.lines || positiveLines)
|
||||||
.filter((l) => l.shippable !== false)
|
.filter((l) => l.shippable !== false)
|
||||||
.map((l) => {
|
.map((l) => {
|
||||||
if (l.sku) return { sku: l.sku, amount: l.amount };
|
if (l.sku) return { sku: l.sku, amount: l.amount };
|
||||||
@@ -337,14 +467,14 @@ export async function handleOrder(req, res, next) {
|
|||||||
const detail = await store.article.getBySku(googleAuthId, sku).catch(() => null);
|
const detail = await store.article.getBySku(googleAuthId, sku).catch(() => null);
|
||||||
const art = detail?.article;
|
const art = detail?.article;
|
||||||
if (!art?.track_stock) continue;
|
if (!art?.track_stock) continue;
|
||||||
const available = art.warehouses?.find((w) => w.id === defaultWarehouseId)?.stock ?? 0;
|
const available = art.warehouses?.find((w) => w.id === whId)?.stock ?? 0;
|
||||||
const shortfall = needed - available;
|
const shortfall = needed - available;
|
||||||
if (shortfall > 0) {
|
if (shortfall > 0) {
|
||||||
await store.stock.adjust(googleAuthId, {
|
await store.stock.adjust(googleAuthId, {
|
||||||
sku,
|
sku,
|
||||||
warehouseId: defaultWarehouseId,
|
warehouseId: whId,
|
||||||
amount: shortfall,
|
amount: shortfall,
|
||||||
text: `POS ${externalOrderNumber || orderNumber}`,
|
text: `POS ${externalOrderNumber || createdOrder.number}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,31 +494,136 @@ export async function handleOrder(req, res, next) {
|
|||||||
orderId: createdOrder.id,
|
orderId: createdOrder.id,
|
||||||
date: validDate,
|
date: validDate,
|
||||||
state: 'delivered',
|
state: 'delivered',
|
||||||
warehouseId: defaultWarehouseId,
|
warehouseId: whId,
|
||||||
lines: shippableLines,
|
lines: shippableLines,
|
||||||
shipping: {
|
shipping: {
|
||||||
carrierId,
|
carrierId,
|
||||||
type: 'selbstabholer',
|
type: 'selbstabholer',
|
||||||
state: 'shipped',
|
state: 'shipped',
|
||||||
warehouseId: defaultWarehouseId,
|
warehouseId: whId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 7b. Stock return & Selbstabholer shipment for Retoure
|
||||||
|
if (createdRetoure) {
|
||||||
|
const whId = await resolveWarehouse();
|
||||||
|
if (whId) {
|
||||||
|
const shippableRetoureLines = (createdRetoure.lines || retoureLines)
|
||||||
|
.filter((l) => l.shippable !== false)
|
||||||
|
.map((l) => {
|
||||||
|
if (l.sku) return { sku: l.sku, amount: l.amount };
|
||||||
|
if (l.id) return { orderLineId: l.id, amount: l.amount };
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
if (shouldDeliver && shippableRetoureLines.length > 0) {
|
||||||
|
const neededBySku = new Map();
|
||||||
|
const shortfallBySku = new Map();
|
||||||
|
for (const l of shippableRetoureLines) {
|
||||||
|
if (l.sku && l.amount > 0) {
|
||||||
|
neededBySku.set(l.sku, (neededBySku.get(l.sku) || 0) + Number(l.amount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const [sku, needed] of neededBySku) {
|
||||||
|
const detail = await store.article.getBySku(googleAuthId, sku).catch(() => null);
|
||||||
|
const art = detail?.article;
|
||||||
|
if (!art?.track_stock) continue;
|
||||||
|
const available = art.warehouses?.find((w) => w.id === whId)?.stock ?? 0;
|
||||||
|
const shortfall = Math.max(0, needed - available);
|
||||||
|
shortfallBySku.set(sku, shortfall);
|
||||||
|
if (shortfall > 0) {
|
||||||
|
await store.stock.adjust(googleAuthId, {
|
||||||
|
sku,
|
||||||
|
warehouseId: whId,
|
||||||
|
amount: shortfall,
|
||||||
|
text: `POS Retoure ${externalOrderNumber || createdRetoure.number || externalOrderId}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const carriers = await store.carrier.list(googleAuthId).catch(() => []);
|
||||||
|
let carrier = carriers.find((c) => c.name?.toLowerCase() === 'selbstabholer') || carriers[0];
|
||||||
|
let carrierId = carrier?.id;
|
||||||
|
if (!carrierId) {
|
||||||
|
const createdCarrier = await store.carrier.create(googleAuthId, {
|
||||||
|
name: 'Selbstabholer',
|
||||||
|
hasPackages: false,
|
||||||
|
});
|
||||||
|
carrierId = createdCarrier.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
await store.shipment.create(googleAuthId, {
|
||||||
|
orderId: createdRetoure.id,
|
||||||
|
date: validDate,
|
||||||
|
state: 'delivered',
|
||||||
|
warehouseId: whId,
|
||||||
|
lines: shippableRetoureLines,
|
||||||
|
shipping: {
|
||||||
|
carrierId,
|
||||||
|
type: 'selbstabholer',
|
||||||
|
state: 'shipped',
|
||||||
|
warehouseId: whId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// stock.ship deducted needed quantity from warehouse stock.
|
||||||
|
// Adjust stock so that the returned articles are properly credited into inventory (+needed).
|
||||||
|
for (const [sku, needed] of neededBySku) {
|
||||||
|
const detail = await store.article.getBySku(googleAuthId, sku).catch(() => null);
|
||||||
|
const art = detail?.article;
|
||||||
|
if (!art?.track_stock) continue;
|
||||||
|
const shortfall = shortfallBySku.get(sku) || 0;
|
||||||
|
const returnAdjustment = 2 * needed - shortfall;
|
||||||
|
if (returnAdjustment > 0) {
|
||||||
|
await store.stock.adjust(googleAuthId, {
|
||||||
|
sku,
|
||||||
|
warehouseId: whId,
|
||||||
|
amount: returnAdjustment,
|
||||||
|
text: `POS Retoure Eingang ${externalOrderNumber || createdRetoure.number || externalOrderId}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const l of retoureLines) {
|
||||||
|
if (!l.sku) continue;
|
||||||
|
const detail = await store.article.getBySku(googleAuthId, l.sku).catch(() => null);
|
||||||
|
const art = detail?.article;
|
||||||
|
if (!art?.track_stock) continue;
|
||||||
|
|
||||||
|
const returnQty = Math.abs(Number(l.amount) || 1);
|
||||||
|
if (returnQty > 0) {
|
||||||
|
await store.stock.adjust(googleAuthId, {
|
||||||
|
sku,
|
||||||
|
warehouseId: whId,
|
||||||
|
amount: returnQty,
|
||||||
|
text: `POS Retoure ${externalOrderNumber || createdRetoure.number || externalOrderId}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 8. Real-time updates
|
// 8. Real-time updates
|
||||||
emit('order', { companyId });
|
if (createdOrder) emit('order', { companyId });
|
||||||
|
if (createdRetoure) emit('retoure', { companyId });
|
||||||
emit('stock', { companyId });
|
emit('stock', { companyId });
|
||||||
emit('ledger', { companyId });
|
emit('ledger', { companyId });
|
||||||
emit('articles', { companyId });
|
emit('articles', { companyId });
|
||||||
emit('shipping', { companyId });
|
if ((createdOrder || createdRetoure) && shouldDeliver) emit('shipping', { companyId });
|
||||||
for (const l of lines) {
|
for (const l of [...positiveLines, ...retoureLines]) {
|
||||||
if (l.sku) {
|
if (l.sku) {
|
||||||
emit(`article-${l.sku}`, { companyId });
|
emit(`article-${l.sku}`, { companyId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`order ${orderNumber} created for externalId=${externalOrderId} (${externalOrderNumber})`);
|
const createdDescs = [];
|
||||||
|
if (createdOrder) createdDescs.push(`order ${createdOrder.number}`);
|
||||||
|
if (createdRetoure) createdDescs.push(`retoure ${createdRetoure.number}`);
|
||||||
|
log.info(`${createdDescs.join(' and ')} created for externalId=${externalOrderId} (${externalOrderNumber})`);
|
||||||
results.push({
|
results.push({
|
||||||
status: 'OK',
|
status: 'OK',
|
||||||
externalOrderId,
|
externalOrderId,
|
||||||
|
|||||||
Reference in New Issue
Block a user