This commit is contained in:
seb
2026-11-01 21:16:07 +01:00
parent 833cee5040
commit 518106ede2
3 changed files with 41 additions and 13 deletions

View File

@@ -14,6 +14,7 @@ import {
parseFullHash,
articleErrorStatus,
interpolateString,
withMandatedOptionsPrice,
interpolateObjects,
} from './shared.js';
@@ -408,7 +409,7 @@ export function register(app, basePath = '') {
let article;
try {
const result = await db.getBySku(req.user.id, sku);
article = result?.article;
article = result?.article ? await withMandatedOptionsPrice(req.user.id, result.article) : undefined;
} catch (err) {
const status = articleErrorStatus(err);
if (status === 500) console.error('Get article by sku failed for print:', err);

View File

@@ -1,4 +1,5 @@
import { emit } from '../../sse.js';
import * as articleDb from '../../../db/article.js';
// #region Helpers
@@ -133,18 +134,42 @@ const BASEPRICE_FORMATTER = new Intl.NumberFormat('de-DE', {
});
/** Formats the price per base unit, e.g. "€ 4,05 / L". Empty when unit data or price is missing. */
export function formatBasePrice(net, taxRate, article) {
export function formatBasePrice(grossRaw, article) {
const unit = article?.unit;
const unitamount = Number(article?.unitamount);
const factor = Number(unit?.basesifactor);
if (net == null || !unit || !(unitamount > 0) || !(factor > 0)) return '';
if (grossRaw == null || !unit?.basesi || !(unitamount > 0) || !(factor > 0)) return '';
const baseAmount = unitamount * factor;
const perBase = (net * (1 + taxRate / 100)) / baseAmount;
const label = factor === 1 ? (unit.symbol || unit.basesi) : unit.basesi;
if (!label) return '';
// Price per base unit equals the price itself: nothing to add.
if (Math.abs(baseAmount - 1) < 1e-9) return '';
const perBase = grossRaw / baseAmount;
const label = factor === 1 && unit.symbol ? unit.symbol : unit.basesi;
return `€ ${BASEPRICE_FORMATTER.format(perBase)} / ${label}`;
}
function grossOf(price) {
const net = Number(price?.net);
if (price?.net == null || !Number.isFinite(net)) return null;
const tax = Number(price.taxRate);
return net * (1 + (Number.isFinite(tax) ? tax : 0) / 100);
}
/**
* Returns the article with `mandatedOptionsGross` set to the summed gross price
* (price × count) of its mandated options, for label placeholders.
*/
export async function withMandatedOptionsPrice(userId, article) {
const mandated = Array.isArray(article?.options) ? article.options.filter((o) => o.mandated) : [];
if (mandated.length === 0) return article;
let extra = 0;
for (const opt of mandated) {
const result = await articleDb.getBySku(userId, opt.sku);
const gross = grossOf(result?.article?.prices?.[0]);
if (gross != null) extra += gross * (Number(opt.count) || 1);
}
return { ...article, mandatedOptionsGross: extra };
}
export function interpolateString(str, article) {
if (typeof str !== 'string') return str;
const basePrice = Array.isArray(article?.prices) && article.prices.length > 0
@@ -156,13 +181,15 @@ export function interpolateString(str, article) {
const taxRate = basePrice?.taxRate != null && Number.isFinite(Number(basePrice.taxRate))
? Number(basePrice.taxRate)
: 0;
const gross = net != null
? Math.round((net * (1 + taxRate / 100) + Number.EPSILON) * 100) / 100
const extra = Number(article?.mandatedOptionsGross) || 0;
const grossRaw = net != null ? net * (1 + taxRate / 100) + extra : null;
const gross = grossRaw != null
? Math.round((grossRaw + Number.EPSILON) * 100) / 100
: null;
return str
.replace(/\{\{name\}\}/gi, article?.name || '')
.replace(/\{\{baseprice\}\}/gi, formatBasePrice(net, taxRate, article))
.replace(/\{\{baseprice\}\}/gi, formatBasePrice(grossRaw, article))
.replace(/\{\{price\}\}/gi, formatPrice(gross));
}

View File

@@ -8,7 +8,7 @@ import { pngToZpl } from '../../services/zplService.js';
import { notifyAgentJob } from '../agent/index.js';
import { emit } from '../sse.js';
import { applyZplCopies } from './zpljob.js';
import { interpolateString, interpolateObjects } from './article/shared.js';
import { interpolateString, interpolateObjects, withMandatedOptionsPrice } from './article/shared.js';
// #region Helpers
@@ -102,7 +102,7 @@ export function register(app, basePath = '') {
const skuStr = String(req.query.sku).trim();
const result = await articleDb.getBySku(req.user.id, skuStr);
if (result?.article) {
article = result.article;
article = await withMandatedOptionsPrice(req.user.id, result.article);
}
}
@@ -220,7 +220,7 @@ export function register(app, basePath = '') {
: null;
if (objects && objects.length > 0) {
try {
const interpolated = interpolateObjects(objects, result.article);
const interpolated = interpolateObjects(objects, await withMandatedOptionsPrice(req.user.id, result.article));
const thumbBuffer = await renderThumb({
width: template.width,
height: template.height,
@@ -655,7 +655,7 @@ export function register(app, basePath = '') {
if (!result?.article) {
return res.status(404).json({ error: 'Article not found' });
}
article = result.article;
article = await withMandatedOptionsPrice(req.user.id, result.article);
} else {
article = {
name: template?.name || 'Musterartikel',