This commit is contained in:
seb
2026-10-17 22:03:24 +02:00
parent 856692b66c
commit 1778815a1e

View File

@@ -413,16 +413,16 @@ export function generateManifestPdf({
// Delivery columns (Wareneingang)
const colDeliv = {
check: 53, checkW: 18,
pos: 71, posW: 18,
thumb: 89, thumbW: 30,
suppSku: 119, suppSkuW: 52,
ownSku: 171, ownSkuW: 92,
ean: 263, eanW: 78,
name: 341, nameW: 85,
deliv: 426, delivW: 38,
stock: 464, stockW: 38,
added: 502, addedW: 40,
pos: 45, posW: 20,
thumb: 67, thumbW: 30,
suppSku: 99, suppSkuW: 52,
ownSku: 153, ownSkuW: 92,
ean: 247, eanW: 78,
name: 327, nameW: 81,
check: 410, checkW: 18,
deliv: 428, delivW: 40,
added: 468, addedW: 42,
stock: 510, stockW: 38,
};
const drawTableHeader = (atY) => {
@@ -435,7 +435,6 @@ export function generateManifestPdf({
.fillColor('#ffffff');
if (isDelivery) {
doc.text('Pos', colDeliv.pos, atY + 6, { width: colDeliv.posW, align: 'center' });
doc.text('Bild', colDeliv.thumb, atY + 6, { width: colDeliv.thumbW, align: 'center' });
doc.text('Lief.-SKU', colDeliv.suppSku, atY + 6, { width: colDeliv.suppSkuW });
@@ -443,8 +442,8 @@ export function generateManifestPdf({
doc.text('EAN', colDeliv.ean, atY + 6, { width: colDeliv.eanW });
doc.text('Bezeichnung', colDeliv.name, atY + 6, { width: colDeliv.nameW });
doc.text('Geliefert', colDeliv.deliv, atY + 6, { width: colDeliv.delivW, align: 'right' });
doc.text('Bestand', colDeliv.stock, atY + 6, { width: colDeliv.stockW, align: 'right' });
doc.text('Gebucht', colDeliv.added, atY + 6, { width: colDeliv.addedW, align: 'right' });
doc.text('Bestand', colDeliv.stock, atY + 6, { width: colDeliv.stockW, align: 'right' });
} else if (showPrices) {
doc.fontSize(8);
doc.text('Pos', colXPrices.pos + 3, atY + 6, { width: colWidthPrices.pos });
@@ -503,7 +502,118 @@ export function generateManifestPdf({
const nameWidth = showPrices ? colWidthPrices.name : colWidthNoPrices.name;
const puSuffix = line.pu && line.pu > 1 ? ` (VPE ${line.pu})` : '';
const nameText = (line.name || 'Position') + puSuffix;
const ownName = (line.ownArticleName || line.articleName || '').trim();
const baseName = (isDelivery && line.ownSku && ownName)
? ownName
: (line.name || 'Position');
const nameText = baseName + puSuffix;
if (isDelivery) {
const rowHeight = 38;
if (y + rowHeight > 730) {
doc.addPage();
y = drawTableHeader(45);
}
if (index % 2 === 1) {
doc.rect(45, y - 2, 505, rowHeight)
.fillColor(lightBg)
.fill();
}
const centerY = y + 12;
// 1. Pos
doc.font('Helvetica')
.fontSize(8.5)
.fillColor(textColor)
.text(String(index + 1), colDeliv.pos, centerY, { width: colDeliv.posW, align: 'center' });
// 2. Thumbpic
if (line.thumbPng) {
try {
doc.image(line.thumbPng, colDeliv.thumb + 2, y + 4, {
fit: [26, 26],
align: 'center',
valign: 'center',
});
} catch (_) {
doc.roundedRect(colDeliv.thumb + 2, y + 4, 26, 26, 2)
.fillColor('#f1f5f9').fill()
.strokeColor('#cbd5e1').lineWidth(0.5).stroke();
}
} else {
doc.roundedRect(colDeliv.thumb + 2, y + 4, 26, 26, 2)
.fillColor('#f1f5f9').fill()
.strokeColor('#cbd5e1').lineWidth(0.5).stroke();
doc.font('Helvetica').fontSize(6).fillColor(mutedColor).text('Kein Bild', colDeliv.thumb + 2, y + 14, { width: 26, align: 'center' });
}
// 3. Supplier SKU
doc.font('Helvetica').fontSize(7.5).fillColor(textColor)
.text(line.supplierSku || '–', colDeliv.suppSku, y + 12, { width: colDeliv.suppSkuW, ellipsis: true });
// 4. Eigene SKU as text + Code128 Barcode
const ownSkuText = line.ownSku || '–';
doc.font('Helvetica-Bold').fontSize(7).fillColor(primaryColor)
.text(ownSkuText, colDeliv.ownSku, y + 2, { width: colDeliv.ownSkuW, ellipsis: true });
if (line.ownSkuBarcode) {
try {
doc.image(line.ownSkuBarcode, colDeliv.ownSku, y + 12, { width: 88, height: 20 });
} catch (_) { }
}
// 5. EAN as barcode
if (line.ean) {
doc.font('Helvetica').fontSize(6.5).fillColor(mutedColor)
.text(line.ean, colDeliv.ean, y + 2, { width: colDeliv.eanW, ellipsis: true });
if (line.eanBarcode) {
try {
doc.image(line.eanBarcode, colDeliv.ean, y + 12, { width: 76, height: 20 });
} catch (_) { }
}
} else {
doc.font('Helvetica').fontSize(8).fillColor(mutedColor)
.text('–', colDeliv.ean, y + 12, { width: colDeliv.eanW, align: 'center' });
}
// 6. Name (own article name if own SKU is connected)
doc.font('Helvetica').fontSize(7.5).fillColor(textColor)
.text(nameText, colDeliv.name, y + 6, { width: colDeliv.nameW });
// 7. Checkbox outline (to cross with pencil) - left of geliefert
doc.rect(colDeliv.check + 3, centerY - 1, 12, 12)
.strokeColor('#64748b')
.lineWidth(0.8)
.stroke();
// 8. Menge geliefert
doc.font('Helvetica-Bold').fontSize(8).fillColor(textColor)
.text(formatQuantity(amount) + ' Stk', colDeliv.deliv, y + 12, { width: colDeliv.delivW, align: 'right' });
// 9. Menge added / gebucht (swapped before bestand)
const hasBooked = line.trackStock && line.addedQuantity != null && line.addedQuantity > 0;
const bookedText = line.trackStock
? (line.addedQuantity != null && line.addedQuantity > 0 ? `+${formatQuantity(line.addedQuantity)} Stk` : (line.addedQuantity === 0 ? '0 Stk' : '–'))
: '–';
doc.font('Helvetica-Bold').fontSize(8).fillColor(hasBooked ? '#16a34a' : mutedColor)
.text(bookedText, colDeliv.added, y + 12, { width: colDeliv.addedW, align: 'right' });
// 10. Menge already in warehouse (bestand)
const stockText = line.trackStock ? (formatQuantity(line.currentStock ?? 0) + ' Stk') : '–';
doc.font('Helvetica').fontSize(7.5).fillColor(line.trackStock ? textColor : mutedColor)
.text(stockText, colDeliv.stock, y + 12, { width: colDeliv.stockW, align: 'right' });
// Row separator
doc.moveTo(45, y + rowHeight - 2)
.lineTo(550, y + rowHeight - 2)
.strokeColor(lightBorder)
.lineWidth(0.5)
.stroke();
y += rowHeight;
return;
}
// Measure actual rendered text heights to prevent overlaps
doc.font('Helvetica').fontSize(8.5);
@@ -542,113 +652,6 @@ export function generateManifestPdf({
const textY = y + 1;
if (isDelivery) {
const rowHeight = 38;
if (y + rowHeight > 730) {
doc.addPage();
y = drawTableHeader(45);
}
if (index % 2 === 1) {
doc.rect(45, y - 2, 505, rowHeight)
.fillColor(lightBg)
.fill();
}
const centerY = y + 12;
// 1. Checkbox outline (to cross with pencil)
doc.rect(colDeliv.check + 3, centerY - 1, 12, 12)
.strokeColor('#64748b')
.lineWidth(0.8)
.stroke();
// 2. Pos
doc.font('Helvetica')
.fontSize(8.5)
.fillColor(textColor)
.text(String(index + 1), colDeliv.pos, centerY, { width: colDeliv.posW, align: 'center' });
// 3. Thumbpic
if (line.thumbPng) {
try {
doc.image(line.thumbPng, colDeliv.thumb + 2, y + 4, {
fit: [26, 26],
align: 'center',
valign: 'center',
});
} catch (_) {
doc.roundedRect(colDeliv.thumb + 2, y + 4, 26, 26, 2)
.fillColor('#f1f5f9').fill()
.strokeColor('#cbd5e1').lineWidth(0.5).stroke();
}
} else {
doc.roundedRect(colDeliv.thumb + 3, y + 4, 26, 26, 2)
.fillColor('#f1f5f9').fill()
.strokeColor('#cbd5e1').lineWidth(0.5).stroke();
doc.font('Helvetica').fontSize(6).fillColor(mutedColor).text('Kein Bild', colDeliv.thumb + 2, y + 14, { width: 26, align: 'center' });
}
// 4. Supplier SKU
doc.font('Helvetica').fontSize(7.5).fillColor(textColor)
.text(line.supplierSku || '–', colDeliv.suppSku, y + 12, { width: colDeliv.suppSkuW, ellipsis: true });
// 5. Eigene SKU as text + Code128 Barcode
const ownSkuText = line.ownSku || line.sku || '–';
doc.font('Helvetica-Bold').fontSize(7).fillColor(primaryColor)
.text(ownSkuText, colDeliv.ownSku, y + 2, { width: colDeliv.ownSkuW, ellipsis: true });
if (line.ownSkuBarcode) {
try {
doc.image(line.ownSkuBarcode, colDeliv.ownSku, y + 12, { width: 88, height: 20 });
} catch (_) { }
}
// 6. EAN as barcode
if (line.ean) {
doc.font('Helvetica').fontSize(6.5).fillColor(mutedColor)
.text(line.ean, colDeliv.ean, y + 2, { width: colDeliv.eanW, ellipsis: true });
if (line.eanBarcode) {
try {
doc.image(line.eanBarcode, colDeliv.ean, y + 12, { width: 76, height: 20 });
} catch (_) { }
}
} else {
doc.font('Helvetica').fontSize(8).fillColor(mutedColor)
.text('–', colDeliv.ean, y + 12, { width: colDeliv.eanW, align: 'center' });
}
// 7. Name
doc.font('Helvetica').fontSize(7.5).fillColor(textColor)
.text(nameText, colDeliv.name, y + 6, { width: colDeliv.nameW });
// 8. Menge geliefert
doc.font('Helvetica-Bold').fontSize(8).fillColor(textColor)
.text(formatQuantity(amount) + ' Stk', colDeliv.deliv, y + 12, { width: colDeliv.delivW, align: 'right' });
// 9. Menge already in warehouse
const stockText = line.trackStock ? (formatQuantity(line.currentStock ?? 0) + ' Stk') : '–';
doc.font('Helvetica').fontSize(7.5).fillColor(line.trackStock ? textColor : mutedColor)
.text(stockText, colDeliv.stock, y + 12, { width: colDeliv.stockW, align: 'right' });
// 10. Menge added / gebucht
const hasBooked = line.trackStock && line.addedQuantity != null && line.addedQuantity > 0;
const bookedText = line.trackStock
? (line.addedQuantity != null && line.addedQuantity > 0 ? `+${formatQuantity(line.addedQuantity)} Stk` : (line.addedQuantity === 0 ? '0 Stk' : '–'))
: '–';
doc.font('Helvetica-Bold').fontSize(8).fillColor(hasBooked ? '#16a34a' : mutedColor)
.text(bookedText, colDeliv.added, y + 12, { width: colDeliv.addedW, align: 'right' });
// Row separator
doc.moveTo(45, y + rowHeight - 2)
.lineTo(550, y + rowHeight - 2)
.strokeColor(lightBorder)
.lineWidth(0.5)
.stroke();
y += rowHeight;
return;
}
if (showPrices) {
doc.font('Helvetica')
.fontSize(8.5)
@@ -981,7 +984,8 @@ export async function createAndAttachManifestPdf(googleAuthId, companyId, manife
manifest.lines = await Promise.all(
manifest.lines.map(async (line) => {
const rawSku = line.sku ? String(line.sku).trim() : '';
let ownSku = rawSku;
let ownSku = '';
let ownArticleName = '';
let supplierSku = '';
let ean = '';
let trackStock = false;
@@ -992,23 +996,23 @@ export async function createAndAttachManifestPdf(googleAuthId, companyId, manife
if (rawSku) {
const findSql = isSqlite
? `SELECT a.id, a.sku, a.track_stock, at.name,
? `SELECT a.id, a.sku, a.track_stock,
(SELECT at.name FROM articletext at WHERE at.article_id = a.id ORDER BY CASE WHEN at.language_id = (SELECT default_language_id FROM company WHERE id = @companyId) THEN 0 ELSE 1 END, at.id ASC LIMIT 1) AS article_name,
(SELECT asup.sku FROM articlesupplier asup WHERE asup.article_id = a.id AND (asup.entity_id = @partnerId OR @partnerId IS NULL) LIMIT 1) AS supplier_sku,
(SELECT ae.ean FROM articleean ae WHERE ae.article_id = a.id ORDER BY ae.id ASC LIMIT 1) AS ean,
(SELECT p.hash FROM articlepic ap INNER JOIN pic p ON p.id = ap.pic_id WHERE ap.article_id = a.id ORDER BY ap.sort ASC LIMIT 1) AS pic_hash,
(SELECT p.smallthumbbinary FROM articlepic ap INNER JOIN pic p ON p.id = ap.pic_id WHERE ap.article_id = a.id ORDER BY ap.sort ASC LIMIT 1) AS smallthumbbinary
FROM article a
LEFT JOIN articletext at ON at.article_id = a.id
WHERE a.company_id = @companyId
AND (a.sku = @sku OR EXISTS (SELECT 1 FROM articlesupplier asup WHERE asup.article_id = a.id AND asup.sku = @sku))
LIMIT 1`
: `SELECT TOP 1 a.id, a.sku, a.track_stock, at.name,
: `SELECT TOP 1 a.id, a.sku, a.track_stock,
(SELECT TOP 1 at.name FROM articletext at WHERE at.article_id = a.id ORDER BY CASE WHEN at.language_id = (SELECT default_language_id FROM company WHERE id = @companyId) THEN 0 ELSE 1 END, at.id ASC) AS article_name,
(SELECT TOP 1 asup.sku FROM articlesupplier asup WHERE asup.article_id = a.id AND (asup.entity_id = @partnerId OR @partnerId IS NULL)) AS supplier_sku,
(SELECT TOP 1 ae.ean FROM articleean ae WHERE ae.article_id = a.id ORDER BY ae.id ASC) AS ean,
(SELECT TOP 1 p.hash FROM articlepic ap INNER JOIN pic p ON p.id = ap.pic_id WHERE ap.article_id = a.id ORDER BY ap.sort ASC) AS pic_hash,
(SELECT TOP 1 p.smallthumbbinary FROM articlepic ap INNER JOIN pic p ON p.id = ap.pic_id WHERE ap.article_id = a.id ORDER BY ap.sort ASC) AS smallthumbbinary
FROM article a
LEFT JOIN articletext at ON at.article_id = a.id
WHERE a.company_id = @companyId
AND (a.sku = @sku OR EXISTS (SELECT 1 FROM articlesupplier asup WHERE asup.article_id = a.id AND asup.sku = @sku))`;
@@ -1017,6 +1021,7 @@ export async function createAndAttachManifestPdf(googleAuthId, companyId, manife
if (art) {
articleId = art.id;
ownSku = art.sku || rawSku;
ownArticleName = (art.article_name || art.name || '').trim();
supplierSku = art.supplier_sku || (rawSku !== art.sku ? rawSku : '');
ean = art.ean || '';
trackStock = Number(art.track_stock ?? 0) === 1;
@@ -1036,6 +1041,8 @@ export async function createAndAttachManifestPdf(googleAuthId, companyId, manife
const stockRes = await store.provider.query(stockSql, { companyId, ownSku, articleId }).catch(() => ({ rows: [] }));
currentStock = Number(stockRes.rows?.[0]?.stock ?? 0);
}
} else {
supplierSku = rawSku;
}
}
@@ -1058,6 +1065,8 @@ export async function createAndAttachManifestPdf(googleAuthId, companyId, manife
return {
...line,
ownSku,
ownArticleName,
articleName: ownArticleName,
supplierSku,
ean,
trackStock,