Files
dashfront/src/utils/price.js
2026-10-07 11:40:55 +02:00

90 lines
3.1 KiB
JavaScript

const PRICE_LOCALE = 'de-DE';
export function getBrutto(netto, kSteuerklasse) {
const rate = { 1: 19, 2: 7, 3: 0 }[kSteuerklasse] ?? 19;
return netto * (1 + rate / 100);
}
/** Currency display, e.g. `6,60 €`. */
export function formatPrice(amount) {
if (amount == null || !Number.isFinite(Number(amount))) return '';
return new Intl.NumberFormat(PRICE_LOCALE, {
style: 'currency',
currency: 'EUR',
}).format(amount);
}
/**
* Amount without currency symbol (for fields with a separate € adornment).
* e.g. `6,60` / `1.234,56`
*/
export function formatPriceAmount(amount, { minFractionDigits = 2, maxFractionDigits = 2 } = {}) {
if (amount == null || !Number.isFinite(Number(amount))) return '';
return new Intl.NumberFormat(PRICE_LOCALE, {
minimumFractionDigits: minFractionDigits,
maximumFractionDigits: maxFractionDigits,
}).format(amount);
}
/**
* Parse de-DE (or plain) price input → number.
* Accepts `6,60`, `1.234,56`, and plain `6.60`.
*/
export function parsePriceInput(value) {
if (value === '' || value == null) return null;
let normalized = String(value).trim().replace(/\s/g, '');
if (normalized === '') return null;
if (normalized.includes(',')) {
// de-DE: thousand `.`, decimal `,`
normalized = normalized.replace(/\./g, '').replace(',', '.');
}
const parsed = Number(normalized);
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
}
/** Convert gross (brutto) → net for PUT /prices. Rounded to 4 decimal places. */
export function netFromGross(gross, taxRate) {
if (gross == null || taxRate == null) return null;
if (!Number.isFinite(gross) || !Number.isFinite(taxRate) || taxRate < 0) return null;
const factor = 1 + taxRate / 100;
if (factor <= 0) return null;
return Math.round((gross / factor) * 10000) / 10000;
}
/**
* Resolves or calculates the gross price (Bruttopreis) for an article.
* Supports:
* - Direct gross price (`article.gross`)
* - Net price with tax rate (`article.net`, `article.taxRate`)
* - Nested prices array (`article.prices`)
*
* @param {object|null} article
* @param {number} [defaultTaxRate=0] Fallback tax rate if not present on article/price
* @returns {number|null} Gross price rounded to 2 decimal places, or null
*/
export function getArticleGross(article, defaultTaxRate = 0) {
if (!article) return null;
if (article.gross != null && Number.isFinite(Number(article.gross))) {
return Number(article.gross);
}
let net = article.net ?? article.price ?? article.vknetto;
let taxRate = article.taxRate;
if (net == null && Array.isArray(article.prices) && article.prices.length > 0) {
const base = article.prices.find((p) => p.minmultiplier === 1) ?? article.prices[0];
if (base?.gross != null && Number.isFinite(Number(base.gross))) {
return Number(base.gross);
}
net = base?.net;
if (taxRate == null) taxRate = base?.taxRate;
}
if (net == null || !Number.isFinite(Number(net))) return null;
const rate = taxRate != null && Number.isFinite(Number(taxRate))
? Number(taxRate)
: Number(defaultTaxRate ?? 0);
const gross = Number(net) * (1 + rate / 100);
return Math.round((gross + Number.EPSILON) * 100) / 100;
}