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.
This commit is contained in:
62
web-frontend/src/format.ts
Normal file
62
web-frontend/src/format.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user