import JsBarcode from "jsbarcode"; import { firstValue } from "../header-footer-section/pdf-shared.utils"; export function buildInvoicePartyDetailsSection(PDFInvoiceData: any, customerLayout: any, printConfig: any = {}) { return buildSalesPartyDetailsSection(PDFInvoiceData, customerLayout, printConfig); } export function buildSalesPartyDetailsSection(PDFInvoiceData: any, customerLayout: any, printConfig: any = {}) { const transactionData = PDFInvoiceData || {}; const billTo = resolveSalesBillToDetails(transactionData); const shipTo = resolveSalesShipToDetails(transactionData); const insurance = resolveInsuranceDetails(transactionData); const useInsurance = !!insurance.name; const shouldShowBillTo = resolvePartyVisibility(printConfig, 'ShowBillTo'); const shouldShowShipTo = useInsurance ? true : resolvePartyVisibility(printConfig, 'ShowShipTo'); const secondaryParty = useInsurance ? insurance : shipTo; const showBarCode = resolveShowBarCode(transactionData, printConfig); const billToCode = resolveBillToCode(transactionData); const otherDetails = resolveOtherDetails(transactionData, printConfig); const shouldShowSecondaryBarcode = shouldShowShipTo && !secondaryParty.hasData && showBarCode && !!billToCode; const secondaryTitle = useInsurance ? 'Insurance' : 'Ship To'; return buildPartyDetailsSectionLayout({ primaryTitle: 'Bill To', primaryParty: shouldShowBillTo ? billTo : { hasData: false }, secondaryTitle, secondaryParty: shouldShowShipTo ? secondaryParty : { hasData: false }, customerLayout, otherDetails, shouldShowSecondaryBarcode, secondaryBarcodeCode: billToCode }); } export function buildPurchasePartyDetailsSection(PDFInvoiceData: any, customerLayout: any, printConfig: any = {}) { const transactionData = PDFInvoiceData || {}; const billTo = resolvePurchaseBFromDetails(transactionData); const shipFrom = resolvePurchaseShipFromDetails(transactionData); const showBarCode = resolveShowBarCode(transactionData, printConfig); const billToCode = resolvePurchaseBillToCode(transactionData); const otherDetails = resolvePurchaseOtherDetails(transactionData, printConfig); const shouldShowSecondaryBarcode = !shipFrom.hasData && showBarCode && !!billToCode; return buildPartyDetailsSectionLayout({ primaryTitle: 'Vendor Details', primaryParty: billTo, secondaryTitle: 'S From', secondaryParty: shipFrom, customerLayout, otherDetails, shouldShowSecondaryBarcode, secondaryBarcodeCode: billToCode }); } function buildPartyDetailsSectionLayout(config: any) { const primaryStack = config?.primaryParty?.hasData ? buildPartyStack(config.primaryTitle, config.primaryParty, config.customerLayout) : [{ text: '' }]; const secondaryStack = config?.secondaryParty?.hasData ? buildPartyStack(config.secondaryTitle, config.secondaryParty, config.customerLayout) : (config?.shouldShowSecondaryBarcode ? buildBarcodeStack(config?.secondaryBarcodeCode) : [{ text: '' }]); const otherDetailsStack = config?.otherDetails?.hasData ? buildOtherDetailsStack(config.otherDetails) : [{ text: '' }]; const hasAnyData = config?.primaryParty?.hasData || config?.secondaryParty?.hasData || config?.shouldShowSecondaryBarcode || config?.otherDetails?.hasData; if (!hasAnyData) { return [{ text: '', margin: [0, 0, 0, 10] }]; } return [{ columns: [ { width: '*', stack: primaryStack }, { width: '*', stack: secondaryStack }, { width: '*', stack: otherDetailsStack } ], columnGap: 10, margin: [0, 0, 0, 10] }]; } function resolveSalesBillToDetails(transactionData: any) { const billTo = transactionData?.BillTo || transactionData?.Customer || transactionData?.Cust || {}; const address = normalizeAddress( billTo?.Adrs || transactionData?.BillTo?.Adrs || transactionData?.BillAddress || transactionData?.BillToAddress || transactionData?.Customer?.Adrs || transactionData?.Cust?.Adrs ); const name = firstValue( billTo?.Name, billTo?.CName, transactionData?.BillTo?.Name, transactionData?.Customer?.Name, transactionData?.Cust?.Name, transactionData?.CustomerName ); const gstin = firstValue( billTo?.TaxId?.Value, billTo?.TaxId?.GSTIN, billTo?.TaxId?.GSTNo ); const contactInfo = resolvePhoneFromContacts(billTo?.Cons); const directPhone = firstValue(billTo?.Ph, billTo?.Phone, billTo?.Mobile); const ownerName = firstValue(transactionData?.Cust?.Name); return { name, addressLines: buildAddressLines(address), gstin, phoneType: contactInfo.type, phoneNumber: contactInfo.number || directPhone, ownerName, hasData: !!(name || gstin || contactInfo.number || directPhone || hasAddress(address)) }; } function resolveSalesShipToDetails(transactionData: any) { const shipTo = transactionData?.ShipTo || transactionData?.ShippmentTo || {}; const address = normalizeAddress( shipTo?.Adrs || transactionData?.ShipAddress || transactionData?.ShipToAddress ); const name = firstValue(shipTo?.Name, shipTo?.CName); const gstin = firstValue(shipTo?.TaxId?.Value, shipTo?.TaxId?.GSTIN, shipTo?.TaxId?.GSTNo); return { name, addressLines: buildAddressLines(address), gstin, hasData: !!(name || gstin || hasAddress(address)) }; } function resolvePurchaseBFromDetails(transactionData: any) { const billTo = transactionData?.BFrom || transactionData?.BillTo || transactionData?.Vendor || transactionData?.Vnd || transactionData?.Supplier || {}; const address = normalizeAddress( billTo?.Adrs || transactionData?.BFrom?.Adrs || transactionData?.BillTo?.Adrs || transactionData?.BFromAddress || transactionData?.BillAddress || transactionData?.BillToAddress || transactionData?.Vendor?.Address || transactionData?.Vnd?.Adrs || transactionData?.Supplier?.Adrs ); const name = firstValue( billTo?.Name, billTo?.CName, transactionData?.BFrom?.Name, transactionData?.BillTo?.Name, transactionData?.Vendor?.Name, transactionData?.Vnd?.Name, transactionData?.Supplier?.Name, transactionData?.BFromName, transactionData?.VendorName ); const gstin = firstValue( billTo?.TaxId?.Value, billTo?.TaxId?.GSTIN, billTo?.TaxId?.GSTNo ); const contactInfo = resolvePhoneFromContacts(billTo?.Contact); const directPhone = firstValue(billTo?.Ph, billTo?.Phone, billTo?.Mobile); return { name, addressLines: buildAddressLines(address), gstin, phoneType: contactInfo.type, phoneNumber: contactInfo.number || directPhone, ownerName: '', hasData: !!(name || gstin || contactInfo.number || directPhone || hasAddress(address)) }; } function resolvePurchaseOtherDetails(transactionData: any, printConfig: any = {}) { const billDate = firstValue(transactionData?.CrDate, transactionData?.InvDt, transactionData?.Date, transactionData?.CrDate); const billNumber = firstValue(transactionData?.Id, transactionData?.Code, transactionData?.InvNo, transactionData?.No); const documentNumberLabel = resolveDocumentNumberLabel(printConfig, 'Bill'); // const productName = [ // transactionData?.Product?.Make, // transactionData?.Product?.Model, // transactionData?.Product?.Var // ] // .map((value: any) => String(value ?? '').trim()) // .filter((value: string) => !!value) // .join(' '); // const vehicleDetails = firstValue( // productName, // transactionData?.VehNo, // transactionData?.VehicleNo, // transactionData?.Vehicle?.RegNo, // transactionData?.Vehicle?.No, // transactionData?.Vehicle?.Details, // transactionData?.VehicleDetails // ); // const Milage = firstValue(transactionData?.MIn, transactionData?.MOut, transactionData?.Milage); // const RegnNo = transactionData?.Product?.RegNo; // const EngNo = transactionData?.Product?.EngNo; // const ChassisNo = transactionData?.Product?.VIN; const lines = [ { label: documentNumberLabel, value: billNumber }, { label: 'Date', value: billDate ? billDate : '' }, // { label: 'Vehicle', value: vehicleDetails }, // { label: 'Regn. No', value: firstValue(RegnNo) }, // { label: 'Engine No', value: firstValue(EngNo) }, // { label: 'Chassis No', value: firstValue(ChassisNo) }, // { label: 'Milage', value: Milage } ].filter((line) => !!line.value); return { lines, hasData: lines.length > 0 }; } function resolvePurchaseShipFromDetails(transactionData: any) { const shipFrom = transactionData?.SFrom || transactionData?.ShipFrom || transactionData?.From || {}; const address = normalizeAddress( shipFrom?.Adrs || transactionData?.SFromAddress || transactionData?.ShipFromAddress || transactionData?.FromAddress ); const name = firstValue( shipFrom?.Name, shipFrom?.CName, shipFrom?.WhName, transactionData?.SFromName, transactionData?.ShipFromName ); const gstin = firstValue(shipFrom?.TaxId?.Value, shipFrom?.TaxId?.GSTIN, shipFrom?.TaxId?.GSTNo); return { name, addressLines: buildAddressLines(address), gstin, hasData: !!(name || gstin || hasAddress(address)) }; } function resolveInsuranceDetails(transactionData: any) { const insurance = transactionData?.Insurance || transactionData?.Ins || transactionData?.Insurer || {}; const address = normalizeAddress(insurance?.Adrs || insurance?.Address); const name = firstValue( insurance?.Name, insurance?.Company, insurance?.Provider, insurance?.InsName ); const gstin = firstValue(insurance?.TaxId?.Value, insurance?.TaxId?.GSTIN, insurance?.TaxId?.GSTNo); return { name, addressLines: buildAddressLines(address), gstin, hasData: !!(name || gstin || hasAddress(address)) }; } function resolveOtherDetails(transactionData: any, printConfig: any = {}) { const invoiceDate = firstValue(transactionData?.CrDate, transactionData?.DDate, transactionData?.InvDt, transactionData?.Date); const invoiceNumber = firstValue(transactionData?._id, transactionData?.Code, transactionData?.InvNo, transactionData?.No); const documentNumberLabel = resolveDocumentNumberLabel(printConfig, 'Invoice'); const productName = [ transactionData?.Product?.Make, transactionData?.Product?.Model, transactionData?.Product?.Var ] .map((value: any) => String(value ?? '').trim()) .filter((value: string) => !!value) .join(' '); const vehicleDetails = firstValue( productName, transactionData?.VehNo, transactionData?.VehicleNo, transactionData?.Vehicle?.RegNo, transactionData?.Vehicle?.No, transactionData?.Vehicle?.Details, transactionData?.VehicleDetails ); const Milage = firstValue(transactionData?.MIn, transactionData?.MOut, transactionData?.Milage); const RegnNo = transactionData?.Product?.RegNo; const EngNo = transactionData?.Product?.EngNo; const ChassisNo = transactionData?.Product?.VIN; const DoctorName = firstValue(transactionData?.Name, transactionData?.DoctorName); const shouldShowDoctorName = documentNumberLabel === 'Sales Receipt#'; const lines = [ { label: documentNumberLabel, value: invoiceNumber }, { label: 'Date', value: invoiceDate ? invoiceDate : '' }, { label: 'Vehicle', value: vehicleDetails }, { label: 'Regn. No', value: firstValue(RegnNo) }, { label: 'Engine No', value: firstValue(EngNo) }, { label: 'Chassis No', value: firstValue(ChassisNo) }, { label: 'Milage', value: Milage }, ...(shouldShowDoctorName ? [{ label: 'Doctor Name', value: DoctorName }] : []) ].filter((line) => !!line.value); return { lines, hasData: lines.length > 0 }; } function buildPartyStack(title: string, party: any, customerLayout: any) { const stack: any[] = [ { text: title, bold: true, fontSize: 9, color: '#374151', margin: [0, 0, 0, 4] } ]; if (party?.name) { stack.push({ text: party.name, bold: true, fontSize: customerLayout?.NameFsize || 9, color: customerLayout?.NameClr || '#111827', // background: '#e5e7eb', margin: [0, 0, 0, 2] }); } else { stack.push({ text: '-', fontSize: 8, color: '#6b7280', margin: [0, 0, 0, 2] }); } const addressLines = Array.isArray(party?.addressLines) ? party.addressLines : []; if (addressLines.length > 0) { addressLines.forEach((line: string) => { stack.push({ text: line, fontSize: 8, lineHeight: 1.05 }); }); } if (party?.gstin) { stack.push({ text: `GSTIN: ${party.gstin}`, fontSize: 8, lineHeight: 1.05 }); } if (party?.phoneNumber) { const phoneLabel = party?.phoneType ? `Phone(${party.phoneType})` : 'Phone'; stack.push({ text: `${phoneLabel} : ${party.phoneNumber}`, fontSize: 8, lineHeight: 1.05 }); } if (title === 'Bill To' && party?.ownerName) { stack.push({ text: [ { text: 'Owner Details', bold: true }, { text: ' : ', bold: true }, { text: party.ownerName, bold: false } ], fontSize: 8.5, color: '#111827', margin: [0, 3, 0, 1] }); // stack.push({ // canvas: [ // { type: 'line', x1: 0, y1: 0, x2: 170, y2: 0, lineWidth: 0.5, lineColor: '#111827' } // ], // margin: [0, 2, 0, 0] // }); } return stack; } function buildOtherDetailsStack(otherDetails: any) { const stack: any[] = []; const lines = Array.isArray(otherDetails?.lines) ? otherDetails.lines : []; if (lines.length > 0) { stack.push({ table: { widths: ['auto', 6, '*'], body: lines.map((line: any) => ([ { text: line.label || '', bold: true, fontSize: 7, color: '#111827', alignment: 'right' }, { text: ':', bold: true, fontSize: 7, color: '#111827', alignment: 'center' }, { text: line.value || '-', fontSize: 7, color: '#111827', alignment: 'left' } ])) }, layout: 'noBorders' }); } return stack; } function buildBarcodeStack(code: string) { const svg = textToSvgBarcode(code); if (!svg) { return [{ text: '' }]; } return [ { svg, width: 80, alignment: 'center', margin: [0, 0, 0, 0] } ]; } function hasAddress(address: any) { const lines = normalizeAddressLines(address); if (lines.length > 0) { return true; } const normalizedAddress = normalizeAddress(address); if (!normalizedAddress) { return false; } return !!firstValue( getAddressFieldValue(normalizedAddress, ['A1', 'Addr1', 'Address1', 'Adrs1', 'Line1', 'Street1']), getAddressFieldValue(normalizedAddress, ['A2', 'Addr2', 'Address2', 'Adrs2', 'Line2', 'Street2']), getAddressFieldValue(normalizedAddress, ['Ct', 'City', 'Town', 'District']), getAddressFieldValue(normalizedAddress, ['St', 'State', 'StateName', 'Province']), getAddressFieldValue(normalizedAddress, ['Pin', 'PIN', 'Zip', 'ZipCode', 'PostalCode', 'Pincode']) ); } function buildAddressLines(address: any) { const lines = normalizeAddressLines(address); if (lines.length > 0) { return lines; } const normalizedAddress = normalizeAddress(address); if (!normalizedAddress) { return []; } const line1 = getAddressFieldValue(normalizedAddress, ['A1', 'Addr1', 'Address1', 'Adrs1', 'Line1', 'Street1']); const line2 = getAddressFieldValue(normalizedAddress, ['A2', 'Addr2', 'Address2', 'Adrs2', 'Line2', 'Street2']); const cityStatePin = [ getAddressFieldValue(normalizedAddress, ['Ct', 'City', 'Town', 'District']), getAddressFieldValue(normalizedAddress, ['St', 'State', 'StateName', 'Province']), getAddressFieldValue(normalizedAddress, ['Pin', 'PIN', 'Zip', 'ZipCode', 'PostalCode', 'Pincode']) ] .filter((value: string) => !!value) .join(', '); return [line1, line2, cityStatePin].filter((value: string) => !!value); } function normalizeAddressLines(address: any) { if (!Array.isArray(address)) { return []; } return address .filter((line: any) => typeof line === 'string') .map((line: string) => line.trim()) .filter((line: string) => !!line); } function normalizeAddress(address: any) { if (!address) { return null; } if (typeof address === 'string') { const line = address.trim(); return line ? { A1: line } : null; } if (Array.isArray(address)) { if (address.length === 0) { return null; } const preferred = address.find((entry: any) => entry?.DefBill || entry?.DefShip || entry?.Def); return preferred || address || null; } if (typeof address === 'object') { if (address?.Adrs && typeof address.Adrs === 'object') { return normalizeAddress(address.Adrs); } if (address?.Address && typeof address.Address === 'object') { return normalizeAddress(address.Address); } return address; } return null; } function getAddressFieldValue(address: any, keys: string[]) { for (const key of keys) { const value = address?.[key]; if (value === undefined || value === null) { continue; } const normalized = String(value).trim(); if (normalized) { return normalized; } } return ''; } // function formatDisplayDate(value: string) { // if (!value) { // return '-'; // } // const dt = new Date(value); // if (!Number.isNaN(dt.getTime())) { // const day = leftPadWithZero(dt.getDate()); // const month = leftPadWithZero(dt.getMonth() + 1); // const year = dt.getFullYear(); // return `${day}/${month}/${year}`; // } // return value; // } // function leftPadWithZero(value: number) { // const text = String(value); // return text.length >= 2 ? text : `0${text}`; // } function resolveShowBarCode(transactionData: any, printConfig: any = {}) { if (Object.prototype.hasOwnProperty.call(printConfig || {}, 'Customer')) { return isTruthy(printConfig?.Customer?.ShowBarCode); } return isTruthy(transactionData?.ShowBarCode) || isTruthy(transactionData?.Entity?.PrCustBar) || isTruthy(transactionData?.PrCustBar); } function resolvePartyVisibility(printConfig: any, key: string) { if (Object.prototype.hasOwnProperty.call(printConfig || {}, key)) { return isTruthy(printConfig?.[key]); } return true; } function resolveBillToCode(transactionData: any) { return firstValue( transactionData?.BillTo?.Code, transactionData?.Customer?.Code, transactionData?.Cust?.Code ); } function resolvePurchaseBillToCode(transactionData: any) { return firstValue( transactionData?.BFrom?.Code, transactionData?.BillTo?.Code, transactionData?.Vendor?.Code, transactionData?.Vnd?.Code, transactionData?.Supplier?.Code ); } function resolveDocumentNumberLabel(printConfig: any, fallbackType: string) { const typeValue = firstValue(printConfig?.Type, fallbackType); const normalizedType = String(typeValue ?? '').trim(); if (!normalizedType) { return `${fallbackType}#`; } const withoutHash = normalizedType.replace(/#+$/g, '').trim(); return `${withoutHash || fallbackType}#`; } function isTruthy(value: any) { if (typeof value === 'boolean') { return value; } if (typeof value === 'number') { return value !== 0; } if (typeof value === 'string') { const normalized = value.trim().toLowerCase(); return normalized === 'true' || normalized === '1' || normalized === 'yes'; } return false; } function textToSvgBarcode(text: string) { if (!text || typeof document === 'undefined') { return ''; } const svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg"); JsBarcode(svgElement, text, { format: "CODE128", width: 1.2, height: 18, margin: 0, marginTop: 0, displayValue: true, fontSize: 6, textMargin: 0, fontOptions: 'bold' }); return svgElement.outerHTML; } function resolvePhoneFromContacts(contacts: any) { if (!Array.isArray(contacts)) { return { type: '', number: '' }; } const preferred = contacts.find((contact: any) => firstValue(contact?.No)); if (!preferred) { return { type: '', number: '' }; } return { type: firstValue(preferred?.Type), number: firstValue(preferred?.No) }; }