Files
company-tool/web-frontend/src/format.ts
Torsten Schulz (local) d5b6f39177 feat: enhance forms with decimal formatting and validation
- Updated CustomersPage.vue to use decimalString for standard discount percent.
- Enhanced IncomingInvoicesPage.vue to format item quantities, unit prices, and tax rates using decimalString.
- Improved ItemsPage.vue with new supplier price management and decimal formatting for prices.
- Modified OrganizationSetupPage.vue to use a dropdown for default tax rates and ensure numeric input for payment days.
- Updated OutgoingInvoicesPage.vue to apply decimal formatting for customer discounts and item details.
- Enhanced PriceImportsPage.vue to include additional fields in the import format.
- Improved PriceRulesPage.vue to use decimal input for markup percentages.
- Updated QuotesPage.vue to apply decimal formatting for customer discounts and item details.
- Enhanced SuppliersPage.vue to use decimal input for standard discount percent.
- Added a new SQL migration to set default unit for items to 'Stck'.
- Introduced format.ts for centralized decimal and currency formatting utilities.
2026-06-03 09:25:10 +02:00

63 lines
2.4 KiB
TypeScript

const decimalFormatter = new Intl.NumberFormat("de-DE", {
minimumFractionDigits: 2,
maximumFractionDigits: 4
});
const currencyFormatter = new Intl.NumberFormat("de-DE", {
style: "currency",
currency: "EUR",
minimumFractionDigits: 2,
maximumFractionDigits: 4
});
export const unitOptions = ["Stck", "kg", "g", "L", "mg", "ml", "qm", "m", "cm", "mm"];
export const taxRateOptions = ["0", "7", "19"];
export function formatDecimal(value: string | number | null | undefined): string {
const number = toNumber(value);
return number === null ? "" : decimalFormatter.format(number);
}
export function formatEuro(value: string | number | null | undefined): string {
const number = toNumber(value);
return number === null ? "-" : currencyFormatter.format(number);
}
export function normalizeDecimal(value: string | number | null | undefined): string | null {
if (value === null || value === undefined || value === "") return null;
const text = String(value).trim();
if (!text) return null;
const compact = text.replace(/\s/g, "");
if (compact.includes(",")) return compact.replace(/\./g, "").replace(",", ".");
const dotCount = (compact.match(/\./g) ?? []).length;
if (dotCount > 1) return compact.replace(/\./g, "");
if (/^-?\d{1,3}\.\d{3}$/.test(compact)) return compact.replace(".", "");
return compact;
}
export function formatDecimalInput<T extends Record<string, unknown>>(target: T, key: keyof T) {
const normalized = normalizeDecimal(target[key] as string | number | null | undefined);
target[key] = (normalized === null ? null : formatDecimal(normalized)) as T[keyof T];
}
export function decimalString(value: string | number | null | undefined, fallback = "0"): string {
return normalizeDecimal(value) ?? fallback;
}
export function normalizeTaxRate(value: string | number | null | undefined, fallback = "19"): string {
const normalized = normalizeDecimal(value);
if (normalized === null) return fallback;
const number = Number(normalized);
if (!Number.isFinite(number)) return normalized;
const compact = String(number);
return taxRateOptions.includes(compact) ? compact : normalized;
}
function toNumber(value: string | number | null | undefined): number | null {
if (value === null || value === undefined || value === "") return null;
const normalized = normalizeDecimal(value);
if (normalized === null) return null;
const number = Number(normalized);
return Number.isFinite(number) ? number : null;
}