Files
alla-allaos-fullstack/src/features/crm/quotations/document/server/pdfme-transforms.ts
2026-06-18 08:58:01 +07:00

144 lines
3.4 KiB
TypeScript

const EMPTY_TABLE_FALLBACK = [['-']];
function normalizeNumber(
amount: number | string | null | undefined
): number | null {
if (typeof amount === 'number' && Number.isFinite(amount)) {
return amount;
}
if (typeof amount === 'string') {
const normalized = Number(amount.replaceAll(',', '').trim());
return Number.isFinite(normalized) ? normalized : null;
}
return null;
}
export function formatPdfDate(
dateString: string | Date | null | undefined,
language: 'th' | 'en' = 'en'
): string {
if (!dateString) {
return '-';
}
const date = dateString instanceof Date ? dateString : new Date(dateString);
if (Number.isNaN(date.getTime())) {
return '-';
}
if (language === 'th') {
return date.toLocaleDateString('th-TH', {
day: 'numeric',
month: 'short',
year: 'numeric',
calendar: 'buddhist'
});
}
return date.toLocaleDateString('en-US', {
day: 'numeric',
month: 'short',
year: 'numeric'
});
}
export function formatPdfCurrency(
amount: number | string | null | undefined,
currencyCode = 'THB'
): string {
const numericAmount = normalizeNumber(amount);
if (numericAmount === null) {
return '-';
}
const normalizedCode = currencyCode.toUpperCase();
const symbols: Record<string, string> = {
THB: 'THB ',
USD: '$',
EUR: 'EUR '
};
const symbol = symbols[normalizedCode] ?? `${normalizedCode} `;
const formatted = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(numericAmount);
return `${symbol}${formatted}`.trim();
}
export function formatTopicItems(topic?: {
items?: Array<{ content?: string | null; sortOrder?: number | null }> | string[];
}): string[][] {
if (!topic?.items?.length) {
return EMPTY_TABLE_FALLBACK;
}
const rows = [...topic.items]
.sort((left, right) => {
if (typeof left === 'string' || typeof right === 'string') {
return 0;
}
return (left.sortOrder ?? Number.MAX_SAFE_INTEGER) - (right.sortOrder ?? Number.MAX_SAFE_INTEGER);
})
.map((item) => {
if (typeof item === 'string') {
return item.trim() || '-';
}
return item?.content?.trim() || '-';
})
.filter(Boolean)
.map((content) => [content]);
return rows.length ? rows : EMPTY_TABLE_FALLBACK;
}
export function normalizePdfmeTable(
value: unknown,
columns?: Array<{ sourceField: string; formatMask?: string | null }>
): string[][] {
if (!Array.isArray(value) || value.length === 0) {
return EMPTY_TABLE_FALLBACK;
}
if (value.every((row) => Array.isArray(row))) {
const normalizedRows = value.map((row) =>
(row as unknown[]).map((cell) => {
const stringValue = String(cell ?? '').trim();
return stringValue || '-';
})
);
return normalizedRows.length ? normalizedRows : EMPTY_TABLE_FALLBACK;
}
if (!columns?.length) {
return value.map((row) => [String(row ?? '').trim() || '-']) || EMPTY_TABLE_FALLBACK;
}
const rows = value.map((row) => {
const rowRecord = row as Record<string, unknown>;
return columns.map((column) => {
const cell = rowRecord[column.sourceField];
if (typeof cell === 'number' || typeof cell === 'string') {
return String(cell).trim() || '-';
}
if (cell === null || cell === undefined) {
return '-';
}
return String(cell).trim() || '-';
});
});
return rows.length ? rows : EMPTY_TABLE_FALLBACK;
}