u
This commit is contained in:
@@ -1,9 +1,74 @@
|
|||||||
import PDFDocument from 'pdfkit';
|
import PDFDocument from 'pdfkit';
|
||||||
|
import { createCanvas } from 'canvas';
|
||||||
|
import JsBarcode from 'jsbarcode';
|
||||||
|
import sharp from 'sharp';
|
||||||
import { getStore } from '../db/store.js';
|
import { getStore } from '../db/store.js';
|
||||||
import * as dbOrder from '../db/order.js';
|
import * as dbOrder from '../db/order.js';
|
||||||
import { emit } from '../api/sse.js';
|
import { emit } from '../api/sse.js';
|
||||||
import { formatAttachment } from '../util/manifestAttachment.js';
|
import { formatAttachment } from '../util/manifestAttachment.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a PNG barcode buffer for embedding in PDFDocument.
|
||||||
|
*
|
||||||
|
* @param {string} text
|
||||||
|
* @param {'CODE128'|'EAN13'|'EAN8'} [preferredFormat='CODE128']
|
||||||
|
* @param {object} [options]
|
||||||
|
* @returns {Buffer|null}
|
||||||
|
*/
|
||||||
|
export function generateBarcodePng(text, preferredFormat = 'CODE128', options = {}) {
|
||||||
|
if (!text) return null;
|
||||||
|
const str = String(text).trim();
|
||||||
|
if (!str) return null;
|
||||||
|
const width = options.width || 120;
|
||||||
|
const height = options.height || 36;
|
||||||
|
try {
|
||||||
|
const canvas = createCanvas(width, height);
|
||||||
|
JsBarcode(canvas, str, {
|
||||||
|
format: preferredFormat,
|
||||||
|
displayValue: options.displayValue ?? true,
|
||||||
|
fontSize: options.fontSize || 9,
|
||||||
|
textMargin: options.textMargin || 1,
|
||||||
|
margin: options.margin || 2,
|
||||||
|
});
|
||||||
|
return canvas.toBuffer('image/png');
|
||||||
|
} catch (e1) {
|
||||||
|
if (preferredFormat !== 'CODE128') {
|
||||||
|
try {
|
||||||
|
const canvas = createCanvas(width, height);
|
||||||
|
JsBarcode(canvas, str, {
|
||||||
|
format: 'CODE128',
|
||||||
|
displayValue: options.displayValue ?? true,
|
||||||
|
fontSize: options.fontSize || 9,
|
||||||
|
textMargin: options.textMargin || 1,
|
||||||
|
margin: options.margin || 2,
|
||||||
|
});
|
||||||
|
return canvas.toBuffer('image/png');
|
||||||
|
} catch (e2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resolveThumbPng(store, googleAuthId, picHash, smallthumbbinary) {
|
||||||
|
if (smallthumbbinary) {
|
||||||
|
try {
|
||||||
|
return await sharp(smallthumbbinary).png().toBuffer();
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
if (picHash) {
|
||||||
|
try {
|
||||||
|
const thumb = await store.pic.getThumbByHash(googleAuthId, picHash).catch(() => null);
|
||||||
|
if (thumb?.buffer) {
|
||||||
|
return await sharp(thumb.buffer).png().toBuffer();
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a Date or date string to German format DD.MM.YYYY.
|
* Formats a Date or date string to German format DD.MM.YYYY.
|
||||||
* @param {Date|string} dateVal
|
* @param {Date|string} dateVal
|
||||||
@@ -84,7 +149,7 @@ const TYPE_CONFIG = {
|
|||||||
showPrices: false,
|
showPrices: false,
|
||||||
filenamePrefix: 'wareneingang',
|
filenamePrefix: 'wareneingang',
|
||||||
introText: 'Wareneingangsdokumentation zu folgenden Positionen:',
|
introText: 'Wareneingangsdokumentation zu folgenden Positionen:',
|
||||||
closingText: 'Wareneingang ordnungsgemäß im Lager erfasst.',
|
closingText: '',
|
||||||
defaultSubject: 'Wareneingang',
|
defaultSubject: 'Wareneingang',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -337,6 +402,8 @@ export function generateManifestPdf({
|
|||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
const showPrices = config.showPrices;
|
const showPrices = config.showPrices;
|
||||||
|
|
||||||
|
const isDelivery = type === 'delivery';
|
||||||
|
|
||||||
// Column setups
|
// Column setups
|
||||||
const colXPrices = { pos: 45, sku: 70, name: 150, amount: 335, gross: 385, tax: 450, total: 482 };
|
const colXPrices = { pos: 45, sku: 70, name: 150, amount: 335, gross: 385, tax: 450, total: 482 };
|
||||||
const colWidthPrices = { pos: 22, sku: 75, name: 180, amount: 45, gross: 60, tax: 30, total: 65 };
|
const colWidthPrices = { pos: 22, sku: 75, name: 180, amount: 45, gross: 60, tax: 30, total: 65 };
|
||||||
@@ -344,16 +411,42 @@ export function generateManifestPdf({
|
|||||||
const colXNoPrices = { pos: 45, sku: 80, name: 185, amount: 455 };
|
const colXNoPrices = { pos: 45, sku: 80, name: 185, amount: 455 };
|
||||||
const colWidthNoPrices = { pos: 30, sku: 100, name: 265, amount: 92 };
|
const colWidthNoPrices = { pos: 30, sku: 100, name: 265, amount: 92 };
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
};
|
||||||
|
|
||||||
const drawTableHeader = (atY) => {
|
const drawTableHeader = (atY) => {
|
||||||
doc.rect(45, atY, 505, 20)
|
doc.rect(45, atY, 505, 20)
|
||||||
.fillColor(primaryColor)
|
.fillColor(primaryColor)
|
||||||
.fill();
|
.fill();
|
||||||
|
|
||||||
doc.font('Helvetica-Bold')
|
doc.font('Helvetica-Bold')
|
||||||
.fontSize(8)
|
.fontSize(7.5)
|
||||||
.fillColor('#ffffff');
|
.fillColor('#ffffff');
|
||||||
|
|
||||||
if (showPrices) {
|
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 });
|
||||||
|
doc.text('Eigene SKU', colDeliv.ownSku, atY + 6, { width: colDeliv.ownSkuW });
|
||||||
|
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' });
|
||||||
|
} else if (showPrices) {
|
||||||
|
doc.fontSize(8);
|
||||||
doc.text('Pos', colXPrices.pos + 3, atY + 6, { width: colWidthPrices.pos });
|
doc.text('Pos', colXPrices.pos + 3, atY + 6, { width: colWidthPrices.pos });
|
||||||
doc.text('Art.-Nr.', colXPrices.sku, atY + 6, { width: colWidthPrices.sku });
|
doc.text('Art.-Nr.', colXPrices.sku, atY + 6, { width: colWidthPrices.sku });
|
||||||
doc.text('Bezeichnung', colXPrices.name, atY + 6, { width: colWidthPrices.name });
|
doc.text('Bezeichnung', colXPrices.name, atY + 6, { width: colWidthPrices.name });
|
||||||
@@ -362,6 +455,7 @@ export function generateManifestPdf({
|
|||||||
doc.text('MwSt', colXPrices.tax, atY + 6, { width: colWidthPrices.tax, align: 'right' });
|
doc.text('MwSt', colXPrices.tax, atY + 6, { width: colWidthPrices.tax, align: 'right' });
|
||||||
doc.text('Gesamt Brutto', colXPrices.total, atY + 6, { width: colWidthPrices.total, align: 'right' });
|
doc.text('Gesamt Brutto', colXPrices.total, atY + 6, { width: colWidthPrices.total, align: 'right' });
|
||||||
} else {
|
} else {
|
||||||
|
doc.fontSize(8);
|
||||||
doc.text('Pos', colXNoPrices.pos + 3, atY + 6, { width: colWidthNoPrices.pos });
|
doc.text('Pos', colXNoPrices.pos + 3, atY + 6, { width: colWidthNoPrices.pos });
|
||||||
doc.text('Art.-Nr.', colXNoPrices.sku, atY + 6, { width: colWidthNoPrices.sku });
|
doc.text('Art.-Nr.', colXNoPrices.sku, atY + 6, { width: colWidthNoPrices.sku });
|
||||||
doc.text('Bezeichnung', colXNoPrices.name, atY + 6, { width: colWidthNoPrices.name });
|
doc.text('Bezeichnung', colXNoPrices.name, atY + 6, { width: colWidthNoPrices.name });
|
||||||
@@ -448,6 +542,113 @@ export function generateManifestPdf({
|
|||||||
|
|
||||||
const textY = y + 1;
|
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) {
|
if (showPrices) {
|
||||||
doc.font('Helvetica')
|
doc.font('Helvetica')
|
||||||
.fontSize(8.5)
|
.fontSize(8.5)
|
||||||
@@ -623,6 +824,33 @@ export function generateManifestPdf({
|
|||||||
y += 16;
|
y += 16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (isDelivery) {
|
||||||
|
if (y + 50 > 750) {
|
||||||
|
doc.addPage();
|
||||||
|
y = 50;
|
||||||
|
} else {
|
||||||
|
y += 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.roundedRect(45, y, 505, 30, 4)
|
||||||
|
.fillColor(lightBg)
|
||||||
|
.fill()
|
||||||
|
.strokeColor(lightBorder)
|
||||||
|
.lineWidth(0.5)
|
||||||
|
.stroke();
|
||||||
|
|
||||||
|
const totalBooked = lines.reduce((sum, l) => sum + Number(l.addedQuantity || 0), 0);
|
||||||
|
const summaryText = `${lines.length} Positionen • Geliefert: ${formatQuantity(totalQty)} Stk • Ins Lager gebucht: ${formatQuantity(totalBooked)} Stk`;
|
||||||
|
|
||||||
|
doc.font('Helvetica-Bold')
|
||||||
|
.fontSize(9)
|
||||||
|
.fillColor(textColor)
|
||||||
|
.text(summaryText, 55, y + 9, {
|
||||||
|
width: 485,
|
||||||
|
align: 'right',
|
||||||
|
});
|
||||||
|
|
||||||
|
y += 45;
|
||||||
} else {
|
} else {
|
||||||
// Non-price summary (total quantity and positions count)
|
// Non-price summary (total quantity and positions count)
|
||||||
if (y + 50 > 750) {
|
if (y + 50 > 750) {
|
||||||
@@ -747,6 +975,102 @@ export async function createAndAttachManifestPdf(googleAuthId, companyId, manife
|
|||||||
|
|
||||||
const partnerId = manifest.entityId ?? manifest.supplierId ?? null;
|
const partnerId = manifest.entityId ?? manifest.supplierId ?? null;
|
||||||
|
|
||||||
|
if (type === 'delivery' && Array.isArray(manifest.lines)) {
|
||||||
|
const isSqlite = store.provider?.dialect === 'sqlite3';
|
||||||
|
|
||||||
|
manifest.lines = await Promise.all(
|
||||||
|
manifest.lines.map(async (line) => {
|
||||||
|
const rawSku = line.sku ? String(line.sku).trim() : '';
|
||||||
|
let ownSku = rawSku;
|
||||||
|
let supplierSku = '';
|
||||||
|
let ean = '';
|
||||||
|
let trackStock = false;
|
||||||
|
let currentStock = null;
|
||||||
|
let addedQuantity = null;
|
||||||
|
let thumbPng = null;
|
||||||
|
let articleId = null;
|
||||||
|
|
||||||
|
if (rawSku) {
|
||||||
|
const findSql = isSqlite
|
||||||
|
? `SELECT a.id, a.sku, a.track_stock, at.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 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))`;
|
||||||
|
|
||||||
|
const artRes = await store.provider.query(findSql, { companyId, sku: rawSku, partnerId }).catch(() => ({ rows: [] }));
|
||||||
|
const art = artRes.rows?.[0];
|
||||||
|
if (art) {
|
||||||
|
articleId = art.id;
|
||||||
|
ownSku = art.sku || rawSku;
|
||||||
|
supplierSku = art.supplier_sku || (rawSku !== art.sku ? rawSku : '');
|
||||||
|
ean = art.ean || '';
|
||||||
|
trackStock = Number(art.track_stock ?? 0) === 1;
|
||||||
|
|
||||||
|
thumbPng = await resolveThumbPng(store, googleAuthId, art.pic_hash, art.smallthumbbinary);
|
||||||
|
|
||||||
|
if (trackStock) {
|
||||||
|
const stockSql = `SELECT COALESCE(SUM(l.amount), 0) AS stock
|
||||||
|
FROM ledger l
|
||||||
|
WHERE l.warehouse_id IS NOT NULL
|
||||||
|
AND l.manifestarticle_id IN (
|
||||||
|
SELECT ma.id FROM manifestarticle ma
|
||||||
|
INNER JOIN manifest m ON m.id = ma.manifest_id
|
||||||
|
WHERE m.company_id = @companyId
|
||||||
|
AND (ma.sku = @ownSku OR ma.sku IN (SELECT asup.sku FROM articlesupplier asup WHERE asup.article_id = @articleId))
|
||||||
|
)`;
|
||||||
|
const stockRes = await store.provider.query(stockSql, { companyId, ownSku, articleId }).catch(() => ({ rows: [] }));
|
||||||
|
currentStock = Number(stockRes.rows?.[0]?.stock ?? 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.id) {
|
||||||
|
const ledgerSql = `SELECT COALESCE(SUM(l.amount), 0) AS added_amount,
|
||||||
|
COUNT(CASE WHEN l.warehouse_id IS NOT NULL THEN 1 END) AS has_warehouse
|
||||||
|
FROM ledger l
|
||||||
|
WHERE l.manifestarticle_id = @lineId
|
||||||
|
AND l.transaction_type IN ('WAREHOUSE', 'ADJUSTMENT')`;
|
||||||
|
const ledgerRes = await store.provider.query(ledgerSql, { lineId: line.id }).catch(() => ({ rows: [] }));
|
||||||
|
const lRow = ledgerRes.rows?.[0];
|
||||||
|
if (lRow && Number(lRow.has_warehouse || 0) > 0) {
|
||||||
|
addedQuantity = Number(lRow.added_amount || 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ownSkuBarcode = ownSku ? generateBarcodePng(ownSku, 'CODE128', { width: 110, height: 26, displayValue: false }) : null;
|
||||||
|
const eanBarcode = ean ? generateBarcodePng(ean, 'EAN13', { width: 100, height: 26, displayValue: false }) : null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...line,
|
||||||
|
ownSku,
|
||||||
|
supplierSku,
|
||||||
|
ean,
|
||||||
|
trackStock,
|
||||||
|
currentStock,
|
||||||
|
addedQuantity,
|
||||||
|
thumbPng,
|
||||||
|
ownSkuBarcode,
|
||||||
|
eanBarcode,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const [company, settings, partner, addresses] = await Promise.all([
|
const [company, settings, partner, addresses] = await Promise.all([
|
||||||
store.company.getById(companyId).catch(() => null),
|
store.company.getById(companyId).catch(() => null),
|
||||||
store.companysetting.list(googleAuthId, { frontendOnly: false, maskWriteonly: false }).catch(() => []),
|
store.companysetting.list(googleAuthId, { frontendOnly: false, maskWriteonly: false }).catch(() => []),
|
||||||
|
|||||||
Reference in New Issue
Block a user