import { pdfTableStyleConfig } from './pdf-table.config'; import { MyDate } from '../../utils/my-date'; export function buildDataRow(item: any, index: number, headerConfig: any[], fixedTo: number = 2) { return headerConfig.map((column: any) => { if (column.type === 'tax') { const taxValue = getTaxValue(item, column.dbField, column.taxKind); const signedTaxValue = shouldRenderNegativeTaxAmount(item, column) ? negateNumericLikeValue(taxValue) : taxValue; const renderedTaxValue = formatNumericLikeValue(signedTaxValue, fixedTo, column.taxKind === 'rate'); return { text: renderedTaxValue, fontSize: pdfTableStyleConfig.bodyFontSize, color: pdfTableStyleConfig.bodyFontColor, alignment: getNumericAlignment(renderedTaxValue) }; } const header = column.header; if (header.text === 'S.No') { const serial = String(index + 1); return { text: serial, fontSize: pdfTableStyleConfig.bodyFontSize, color: pdfTableStyleConfig.bodyFontColor, alignment: getNumericAlignment(serial) }; } const mainValue = formatFieldValue(item, header, fixedTo); const stackedLines = (header.stackFields ?? []) .map((stackField: any) => { const stackValue = formatFieldValue(item, stackField, fixedTo); if (!stackValue) { return null; } return { text: `${stackField.text}: ${stackValue}`, fontSize: pdfTableStyleConfig.stackFieldFontSize, color: pdfTableStyleConfig.bodyFontColor, italics: true }; }) .filter((line: any) => !!line); if (stackedLines.length === 0) { return { text: mainValue, fontSize: pdfTableStyleConfig.bodyFontSize, color: pdfTableStyleConfig.bodyFontColor, alignment: getCellAlignment(header.text, mainValue) }; } return { stack: [ { text: mainValue, fontSize: pdfTableStyleConfig.bodyFontSize, color: pdfTableStyleConfig.bodyFontColor }, ...stackedLines ] }; }); } function formatFieldValue(item: any, fieldConfig: any, fixedTo: number) { const rawValue = getFirstFieldValue(item, fieldConfig?.dbFields); if (isExpiryField(fieldConfig)) { return formatExpiryDateValue(rawValue); } const signedValue = shouldRenderNegativeField(item, fieldConfig) ? negateNumericLikeValue(rawValue) : rawValue; return formatNumericLikeValue(signedValue, fixedTo, shouldSkipFixedTo(fieldConfig)); } function getTaxValue(item: any, taxName: 'CGST' | 'SGST' | 'IGST' | 'GST', taxKind: 'rate' | 'amount') { if (taxName === 'GST') { if (taxKind === 'rate') { return getCombinedTaxValue(item, ['CombinedTaxPercentage', 'GSTPerc'], [ ['CGSTPerc', 'CGST'], ['SGSTPerc', 'SGST'], ['IGSTPerc', 'IGST'] ]); } return getCombinedTaxValue(item, ['TotalTaxAmount', 'TaxAmount', 'GSTAmt'], [ ['CGSTAmt', 'CGST'], ['SGSTAmt', 'SGST'], ['IGSTAmt', 'IGST'] ]); } if (taxName === 'IGST') { const fields = taxKind === 'rate' ? ['IGSTPerc'] : ['IGSTAmt']; return getFirstFieldValue(item, fields); } const fields = taxKind === 'rate' ? [`${taxName}Perc`, taxName] : [`${taxName}Amt`, taxName]; return getFirstFieldValue(item, fields); } function getCombinedTaxValue(item: any, combinedFields: string[], componentFieldGroups: string[][]) { const combinedValue = getFirstFieldValue(item, combinedFields); if (combinedValue) { return combinedValue; } const values = componentFieldGroups .map((fields: string[]) => getFirstFieldValue(item, fields)) .filter((value: string) => value !== ''); if (values.length === 0) { return ''; } const numericValues = values.map((value: string) => Number(value.replace(/,/g, '').trim())); if (numericValues.some((value: number) => !Number.isFinite(value))) { return values[0]; } return String(numericValues.reduce((sum: number, value: number) => sum + value, 0)); } function shouldRenderNegativeTaxAmount(item: any, column: any) { return !!item?.Ret && column?.taxKind === 'amount'; } function shouldRenderNegativeField(item: any, fieldConfig: any) { if (!item?.Ret) { return false; } const headerText = String(fieldConfig?.text ?? '').trim().toUpperCase(); return headerText === 'QTY' || headerText === 'OFFER QTY' || headerText === 'PRICE' || headerText === 'LINE TOTAL'; } function negateNumericLikeValue(value: string) { if (!isNumericLike(String(value ?? ''))) { return value; } const normalized = Number(String(value).replace(/,/g, '').trim()); if (!Number.isFinite(normalized)) { return value; } return String(normalized === 0 ? 0 : -Math.abs(normalized)); } export function getFirstFieldValue(item: any, dbFields: string[] = []) { for (const field of dbFields) { const value = readValue(item, field); if (value !== undefined && value !== null && value !== '') { return String(value); } } return ''; } function readValue(item: any, field: string) { if (!item || !field) { return undefined; } if (item[field] !== undefined) { return item[field]; } const dottedPathValue = field.split('.').reduce((current: any, key: string) => { if (current && current[key] !== undefined) { return current[key]; } return undefined; }, item); if (dottedPathValue !== undefined) { return dottedPathValue; } return deepFindByKey(item, field); } function deepFindByKey(source: any, field: string): any { if (!source || typeof source !== 'object') { return undefined; } if (source[field] !== undefined) { return source[field]; } for (const key of Object.keys(source)) { const value = source[key]; if (value && typeof value === 'object') { const nestedValue = deepFindByKey(value, field); if (nestedValue !== undefined) { return nestedValue; } } } return undefined; } export function getNumericAlignment(value: string) { return isNumericLike(value) ? 'right' : 'left'; } export function getCellAlignment(headerText: string, value: string) { if (isRightAlignedHeader(headerText)) { return 'right'; } if (isLeftAlignedHeader(headerText)) { return 'left'; } return getNumericAlignment(value); } function isNumericLike(value: string) { if (!value) { return false; } const normalized = value.replace(/,/g, '').trim(); return /^-?\d+(\.\d+)?$/.test(normalized); } function isLeftAlignedHeader(headerText: string) { return false; } function isRightAlignedHeader(headerText: string) { const normalizedHeader = String(headerText ?? '').replace(/\s+/g, '').trim().toUpperCase(); return normalizedHeader === 'QTY' || normalizedHeader === 'OFFERQTY'; } function formatNumericLikeValue(value: string, fixedTo: number, skipFixedTo: boolean = false) { if (skipFixedTo) { return value; } if (!isNumericLike(value)) { return value; } const normalized = Number(String(value).replace(/,/g, '').trim()); if (!Number.isFinite(normalized)) { return value; } return normalized.toFixed(fixedTo); } function isExpiryField(fieldConfig: any) { const text = String(fieldConfig?.text ?? '').trim().toUpperCase(); if (text === 'EXPIRY' || text === 'EXPIRY DATE' || text === 'EXDT') { return true; } const dbFields = Array.isArray(fieldConfig?.dbFields) ? fieldConfig.dbFields : []; return dbFields.some((field: any) => { const normalized = String(field ?? '').trim().toUpperCase(); return normalized === 'EXDT' || normalized.endsWith('.EXDT') || normalized.endsWith('.EXPIRY'); }); } function formatExpiryDateValue(value: string) { if (!value) { return value; } const input = String(value).trim(); if (!input) { return ''; } if (/^\d{2}\/\d{2}$/.test(input)) { return input; } const parsed = new Date(input); if (Number.isNaN(parsed.getTime())) { return input; } return MyDate.ConvertUTCDateToReadableExDate(input) || input; } function shouldSkipFixedTo(header: any) { const text = String(header?.text ?? '').trim().toUpperCase(); const normalizedText = text.replace(/\s+/g, ''); if (text === 'HSN/SAC' || text === 'BN' || text === 'BNO' || text.includes('%') || normalizedText === 'QTY' || normalizedText === 'OFFERQTY' || normalizedText === 'OFQTY' || normalizedText === 'MPN') { return true; } const dbFields = Array.isArray(header?.dbFields) ? header.dbFields : []; return dbFields.some((field: any) => { const normalized = String(field ?? '').trim().toUpperCase(); return normalized === 'BN' || normalized === 'BNO' || normalized === 'QTY' || normalized === 'OFQTY' || normalized === 'MPN' || normalized.endsWith('.BN') || normalized.endsWith('.QTY') || normalized.endsWith('.OFQTY') || normalized.endsWith('.MPN') || normalized.endsWith('PERC') || normalized.endsWith('.PERC'); }); }