Pakistan Digital Invoicing Solution (for Manager.io Users)

That is the invoice number you shared, and I am a bit confused about you checking it in the POS invoice verification. Does B2B use the same application to check invoices?

Invoices are correctly reported to FBR, QR Code is Generated and Invoice No. but at digital dashboard it is not reflecting the invoices. see image.

FBR has not provided any separate application to check B2B invoices for digital invoices.

So, main problem is, invoices not reflected in portal. if the invoice is uploaded into FBR digital portal, than it is valid.

I have upload the same invoice in SANDBOX testing, this invoice is correctly upload at FBR portal. afterwards, The same procedure used for LIVE and change TOKEN key, but it is not reflected into portal.

Would you mind testing the Sandbox Environment using your Production Token? There’s a possibility that the data was submitted to the Sandbox Portal instead. Please check if the invoice is available there.

I have already check that, these invoices are not showing at sandbox testing.

1 Like

Yes, that shouldn’t happen, as each environment has its own token.

I can’t say for sure why an invoice that was successfully submitted and received an invoice number from the server isn’t appearing in the portal.

You might want to contact FBR directly and provide them with the invoice number you received.

Alternatively, you could wait to see if other users who are already using the Production Environment have experienced a similar issue.

I have already E-MAILED to technical digital department regarding this issue. I dont know that anyone using the production or live mode for digital invoicing. any reply from FBR should be shared.

PLEASE CHECK MY OTHER ISSUE AT ABOVE, REGARDING CUSTOM INVOICE THEME

I did face this issue in sandbox yesterday. I submitted an invoice with SN019, it got invoice number from FBR, but My FBR portal did not reflect it. I reported another invoice with different tax rate and it got visible on FBR portal. But after a couple of hours, the missing invoice became visible on FBR portal. The issue is not only with production, it is also observed in Sandbox.

1 Like

Since you’re using a custom theme, it’s your responsibility to ensure that the theme supports displaying the QR Code (Image Custom Field - d2e9265a-460e-4a06-83f9-29a523a4d516).

For your information, the default custom theme code displays the QR Code correctly. You may want to review the code in your custom theme to ensure it’s implemented properly.

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	
</head>
<body>
	<!-- MAIN LAYOUT TABLE - Used to ensure proper page breaks and header repetition -->
	<table>
		
		<tbody>
			<!-- NON-REPEATING CONTENT SECTION -->
			<tr>
				<td>

					<!-- QR CODE SECTION - For special features like Saudi Arabia e-invoicing -->
					<script src="resources/qrcode/qrcode.js"></script>
					<div id="qrcode" style="margin-bottom: 20px"></div>

					<!-- CUSTOM FIELDS SECTION - Notes, terms, and other custom content -->
					<div id="custom-fields"></div>

				</td>
			</tr>
		</tbody>
	</table>

	<!-- JAVASCRIPT SECTION - Handles dynamic content population -->
	<script>
		/**
		 * Sends resize message to parent frame when content changes
		 * This ensures the iframe container adjusts to content height
		 */
		function sendResize() {
			window.parent.postMessage({
				type: "resize",
				width: document.documentElement.scrollWidth + 1,
				height: document.documentElement.scrollHeight + 1
			}, "*");
		}

		/**
		 * Main message listener - receives document data from parent frame
		 * The parent sends all invoice/document data via postMessage
		 */
		window.addEventListener("message", (event) => {

			// Security: Only accept messages from parent frame
			if (event.source !== window.parent) return;
			// Only process context-response messages
			if (event.data.type !== 'context-response') return;

			// Extract the main data object sent from parent
			// This contains all document information (business, recipient, items, etc.)
			const data = event.data.body;

			// CUSTOM FIELDS
			// Display custom fields like notes, terms, payment instructions, etc.
			const customFieldsDiv = document.getElementById("custom-fields");
			customFieldsDiv.innerHTML = "";
			(data.custom_fields || []).forEach(f => {
				// Some custom fields can be displayed at the top with other document fields
				if (f.displayAtTheTop) {
					// Add to the fields section at the top of document
					const dt = document.createElement("dt");
					dt.innerHTML = f.label;
					const dd = document.createElement("dd");
					dd.innerHTML = f.text;
					fieldsDiv.appendChild(dt);
					fieldsDiv.appendChild(dd);
				}
				else {
					// Display as a labeled section below the table
					const div = document.createElement("div");
					div.innerHTML = `<strong>${f.label || ""}</strong><br />${(f.text || "").split("\n").join("<br />")}<br /><br />`;
					customFieldsDiv.appendChild(div);
				}
			});

			// Notify parent frame of content size after rendering
			sendResize();
		}, false);

		/**
		 * Initialize communication with parent frame
		 * Requests document data when page loads
		 */
		window.addEventListener("load", () =>
			window.parent.postMessage({ type: "context-request" }, "*")
		);
	</script>

</body>
</html>

Hello,

As per telephonic conversation and discussion with the Technical person of DIGITAL INVOICING, MEHBOOB SB.

He said me, some business PRODUCTION TOKEN given to taxpayers but not enable from our department due to sandbox testing. After enable it, all invoices are reflect in Dashboard.

Furthermore, he also guide me regarding the display of QR Code, the display of QR must like attached image also showing the FBR logo and Invoice No at the bottom.

Can you please guide me how can i do so. something like that

2 Likes

I have share a Custom Invoice Theme Code, Kindly check that why our QR Code is not display and display of QR code as above mentioned.

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	<style>
		@page { margin: 20mm; }
		*, ::after, ::before, ::backdrop, ::file-selector-button { margin: 0; padding: 0; }
		*, ::after, ::before, ::backdrop, ::file-selector-button { box-sizing: border-box; border: 0 solid; }
		body {
			margin: 0;
			padding: 30px;
			font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
			color: #171717;
			font-size: 12px;
			line-height: 1.428571429;
			min-width: 800px;
		}
		address {
			font-style: normal;
			line-height: 1.5em;
			font-weight: bold;
			text-transform: uppercase;
		}
		dt {
			font-weight: bold;
			margin: 0 0 2px 0;
		}
		dd { margin: 0 0 16px 0; }
		dd:last-of-type { margin-bottom: 0; }
		@media print { body { padding: 0; min-width: auto; } }
		table { font-size: 12px; width: 100%; }
		tr#table-headers th {
			font-weight: bold;
			padding: 5px 10px;
			border: 1px solid #000;
			text-align: start;
			text-transform: uppercase;
		}
		tbody#table-rows td {
			padding: 5px 10px;
			text-align: start;
			vertical-align: top
		}
		tbody#table-rows tr.row td { border-left: 1px solid #000; border-right: 1px solid #000; }
		tbody#table-rows tr.last-row td { padding-bottom: 30px; border-bottom: 1px solid #000; }
		tbody#table-rows tr.column-total td {
			font-weight: bold;
			border: 1px solid #000;
			white-space: nowrap;
			text-align: right;
		}
		tbody#table-rows tr.total td { white-space: nowrap; }
		tbody#table-rows tr.total td:first-child { text-align: end; }
		tbody#table-rows tr.total td:last-child { border: 1px solid #000; text-align: right; }
		.words-in-amount { font-weight: bold; font-style: italic; font-size: 13px; margin-top: 10px; }
		.signature-block {
			display: flex; justify-content: space-between; margin-top: 50px; gap:60px;
		}
		.signature-space {
			flex: 1 0 200px;
			text-align: center;
		}
		.signature-line {
			border-top: 2px solid #000; margin: 0 auto 8px auto; width: 180px; height: 2px;
		}
		.signature-label {
			font-weight: bold; text-transform: uppercase; letter-spacing: 1px; font-size: 13px;
		}
	</style>
</head>
<body>
	<table>
		<thead>
			<tr>
				<td>
					<header style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 20px">
						<h1 id="title" style="font-size: 32px; line-height: 32px; font-weight: bold"></h1>
						<div id="business-logo" style="text-align: end"></div>
					</header>
					<section style="display: flex; margin-bottom: 20px; width: 100%; align-items: flex-start; gap: 20px">
						<address id="recipient-info" style="flex: 1"></address>
						<dl id="fields" style="flex: 1; text-align: end"></dl>
						<div aria-hidden="true" style="width: 1px; border-left: 1px solid #000; align-self: stretch "></div>
						<address id="business-info" style="white-space: nowrap"></address>
					</section>
					<p style="font-weight: bold; font-size: 14px; margin-bottom: 20px" id="description"></p>
				</td>
			</tr>
		</thead>
		<tbody>
			<tr>
				<td>
					<table style="border-collapse: collapse; width: 100%">
						<thead>
							<tr id="table-headers"></tr>
						</thead>
						<tbody id="table-rows"></tbody>
					</table>
					<script src="resources/qrcode/qrcode.js"></script>
					<div id="qrcode" style="margin-bottom: 20px"></div>
					<div id="custom-fields"></div>
					<div id="words-in-amount" class="words-in-amount"></div>
					<table><tr><td><div id="footers"></div></td></tr></table>
					<div id="status" style="text-align: center"></div>
					<!-- SIGNATURES -->
					<div class="signature-block">
						<div class="signature-space">
							<div class="signature-line"></div>
							<span class="signature-label">PREPARED BY</span>
						</div>
						<div class="signature-space">
							<div class="signature-line"></div>
							<span class="signature-label">APPROVED BY</span>
						</div>
					</div>
					<!-- END SIGNATURES -->
				</td>
			</tr>
		</tbody>
	</table>
	<script>
	// COLUMN MAPPING: Defines the columns for the table as required
	const predefinedColumns = [
		{ key: 'sr', label: 'SR', align: 'center', property: 'SR' },
		{ key: 'item', label: 'ITEM', align: 'start', property: 'Item' },
		{ key: 'qty', label: 'QTY', align: 'center', property: 'Qty' },
		{ key: 'unit_price', label: 'UNIT PRICE', align: 'right', property: 'Unit price' },
		{ key: 'amount', label: 'AMOUNT', align: 'right', property: 'Amount' },
		{ key: 'rate', label: 'RATE', align: 'center', property: 'Tax' },
		{ key: 'sales_tax', label: 'SALES TAX', align: 'right', property: 'Tax Amount' },
		{ key: 'total', label: 'TOTAL', align: 'right', property: 'Total' }
	];
	function extractTaxRate(text) {
		if(!text) return text;
		var match = text.match(/(\d+(?:\.\d+)?)%/);
		return match ? match[1] + '%' : text;
	}
	window.addEventListener("message", (event) => {
		if (event.source !== window.parent) return;
		if (event.data.type !== 'context-response') return;
		const data = event.data.body;
		document.documentElement.dir = data.direction;
		document.title = [data?.business?.name, data?.title, data?.reference].filter(Boolean).join(' - ');
		// BUSINESS INFO SECTION
		const business = data.business || {};
		var businessNameUC = (business.name || "").toUpperCase();
		var businessAddressUC = (business.address ? business.address.toUpperCase().replace(/\n/g, "<br>") : "");
		// Both business name and address in same font-weight (no <strong>)
		document.getElementById("business-info").innerHTML = `${businessNameUC}<br>${businessAddressUC}`;
		// RECIPIENT INFO SECTION
		const recipient = data.recipient || {};
		var recipientNameUC = (recipient.name || "").toUpperCase();
		var recipientAddressUC = (recipient.address ? recipient.address.toUpperCase().replace(/\n/g, "<br>") : "");
		// Both recipient name and address in same font-weight (no <strong>)
		document.getElementById("recipient-info").innerHTML = `${recipientNameUC}<br>${recipientAddressUC}`;
		document.getElementById("title").innerHTML = data.title ? data.title.toUpperCase() : "NO TITLE";
		document.getElementById("description").innerHTML = data.description ? data.description.toUpperCase() : "";
		// BUSINESS LOGO
		var businessLogoTd = document.getElementById("business-logo");
		if (business.logo) {
			const img = document.createElement("img");
			img.addEventListener("load", sendResize);
			img.src = business.logo;
			img.style = "max-height: 150px; max-width: 300px; display: inline";
			businessLogoTd.appendChild(img);
		}
		// FIELDS
		const fieldsDiv = document.getElementById("fields");
		fieldsDiv.innerHTML = "";
		(data.fields || []).forEach(f => {
			const dt = document.createElement("dt");
			dt.innerHTML = f.label.toUpperCase();
			const dd = document.createElement("dd");
			dd.innerHTML = (f.text || "").toUpperCase();
			fieldsDiv.appendChild(dt);
			fieldsDiv.appendChild(dd);
		});
		// TABLE HEADERS: Use predefinedColumns always
		const headersRow = document.getElementById("table-headers");
		headersRow.innerHTML = "";
		predefinedColumns.forEach(col => {
			const th = document.createElement("th");
			th.innerHTML = col.label;
			th.style.textAlign = col.align;
			if (col.label === 'SR' || col.label === 'QTY') { th.style.whiteSpace = 'nowrap'; th.style.width = '1px'; }
			if (col.label === 'UNIT PRICE' || col.label === 'AMOUNT' || col.label === 'TOTAL' || col.label === 'SALES TAX') th.style.whiteSpace = 'nowrap';
			headersRow.appendChild(th);
		});
		// Map Manager.io columns to new column specification
		function findColumn(property) {
			const userCols = (data.table.columns||[]);
			for (let i=0;i<userCols.length;i++) {
				if ((userCols[i].label||'').replace(/\s+/g,'').toUpperCase() === property.replace(/\s+/g,'').toUpperCase())
					return {col:userCols[i], idx:i};
			}
			return null;
		}
		// LINE ITEMS
		const rowsBody = document.getElementById("table-rows");
		rowsBody.innerHTML = "";
		(data.table.rows || []).forEach(row => {
			const tr = document.createElement("tr");
			tr.className = 'row';
			predefinedColumns.forEach(def => {
				const colFound = findColumn(def.property);
				const td = document.createElement("td");
				if (colFound) {
					let displayText = row.cells[colFound.idx]?.text || '';
					// if this is RATE, show just percent
					if (def.label === 'RATE' && displayText) displayText = extractTaxRate(displayText);
					td.innerHTML = displayText.split("\n").join("<br />");
					td.style.textAlign = def.align;
					if(['SR','QTY'].includes(def.label)) td.style.whiteSpace='nowrap';
					if(['UNIT PRICE','AMOUNT','TOTAL','SALES TAX'].includes(def.label)) td.style.whiteSpace='nowrap';
				} else {
					td.innerHTML = '';
					td.style.textAlign = def.align;
				}
				tr.appendChild(td);
			});
			rowsBody.appendChild(tr);
		});
		// Last row style
		const rows = rowsBody.querySelectorAll('tr.row');
		if (rows.length > 0) { rows[rows.length - 1].classList.add('last-row'); }
		// COLUMN TOTALS
		const trSum = document.createElement("tr");
		trSum.classList.add('column-total');
		predefinedColumns.forEach(def => {
			const colFound = findColumn(def.property);
			const td = document.createElement("td");
			td.style.textAlign = def.align;
			if (colFound && data.table.columns[colFound.idx].sumText) {
				td.innerHTML = data.table.columns[colFound.idx].sumText;
			} else {
				td.innerHTML = '';
			}
			trSum.appendChild(td);
		});
		if (trSum.innerText.trim()) rowsBody.appendChild(trSum);
		// TABLE TOTALS
		(data.table.totals || []).forEach(total => {
			const tr = document.createElement("tr");
			tr.className = 'total';
			const tdLabel = document.createElement("td");
			let labelText = total.label.toUpperCase();
			if(labelText.match(/^(SALESTAX|GST|VAT)[^\d]*\d+%?$/)) labelText = 'SALES TAX';
			tdLabel.innerHTML = labelText;
			tdLabel.colSpan = predefinedColumns.length - 1;
			const tdValue = document.createElement("td");
			tdValue.innerHTML = total.text;
			tdValue.id = total.key;
			if (total.class) tdValue.classList.add(total.class);
			tdValue.dataset.value = total.number;
			if (total.emphasis) {
				tdLabel.style.fontWeight = 'bold';
				tdValue.style.fontWeight = 'bold';
			}
			tr.appendChild(tdLabel);
			tr.appendChild(tdValue);
			rowsBody.appendChild(tr);
		});
		// Display words-in-amount just after the table
		const amountWordsDiv = document.getElementById('words-in-amount');
		amountWordsDiv.innerHTML = '';
		if(data.custom_fields && data.custom_fields.length > 0) {
			const found = data.custom_fields.find(f=>f.label && f.label.toLowerCase().includes('amount in words'));
			if(found && found.text) {
				amountWordsDiv.innerHTML = `<span>${found.text}</span>`;
			}
		}
		if(amountWordsDiv.firstElementChild) {
			amountWordsDiv.firstElementChild.style.fontWeight = 'bold';
			amountWordsDiv.firstElementChild.style.fontStyle = 'italic';
			amountWordsDiv.firstElementChild.style.textTransform = 'none';
		}
		// Custom fields ordinary display, except "amount in words" suppressed
		const customFieldsDiv = document.getElementById("custom-fields");
		customFieldsDiv.innerHTML = "";
		(data.custom_fields || []).forEach(f => {
			if (f.label && f.label.toLowerCase().includes('amount in words')) return;
			if (f.displayAtTheTop) {
				const dt = document.createElement("dt");
				dt.innerHTML = f.label.toUpperCase();
				const dd = document.createElement("dd");
				dd.innerHTML = (f.text || "").toUpperCase();
				fieldsDiv.appendChild(dt);
				fieldsDiv.appendChild(dd);
			} else {
				const div = document.createElement("div");
				div.innerHTML = `<strong>${f.label ? f.label.toUpperCase() : ""}</strong><br />${(f.text || "").split("\n").join("<br />").toUpperCase()}<br /><br />`;
				customFieldsDiv.appendChild(div);
			}
		});
		const footersDiv = document.getElementById("footers");
		footersDiv.innerHTML = "";
		(data.footers || []).forEach(f => {
			const div = document.createElement("div");
			div.style = 'margin-top: 20px';
			div.innerHTML = f;
			footersDiv.appendChild(div);
			const scripts = div.querySelectorAll("script");
			scripts.forEach(script => {
				const newScript = document.createElement("script");
				for (const attr of script.attributes) {
					newScript.setAttribute(attr.name, attr.value);
				}
				if (script.textContent) {
					newScript.textContent = script.textContent;
				}
				script.parentNode.replaceChild(newScript, script);
			});
		});
		const statusDiv = document.getElementById("status");
		if (data.emphasis?.text != null) {
			statusDiv.style.marginTop = '40px';
			const span = document.createElement("span");
			span.style = 'border-width: 5px; border-color: #FF0000; border-style: solid; padding: 10px; font-size: 20px; text-transform: uppercase';
			if (data.emphasis.positive) { span.style.color = 'green'; span.style.borderColor = 'green'; }
			if (data.emphasis.negative) { span.style.color = 'red'; span.style.borderColor = 'red'; }
			span.innerHTML = data.emphasis.text.toUpperCase();
			statusDiv.appendChild(span);
		}
		sendResize();
	}, false);
	window.addEventListener("load", () =>
		window.parent.postMessage({ type: "context-request" }, "*")
	);
	function sendResize() {
		window.parent.postMessage({
			type: "resize",
			width: document.documentElement.scrollWidth + 1,
			height: document.documentElement.scrollHeight + 1
		}, "*");
	}
	</script>
</body>
</html>

You means like This ?


You need to change some code in theme and apply it.

Following is code part when displaying custom fields in theme :slight_smile:
else {
// Display as a labeled section below the table
if (f.label === “QR Code”) {
const logoBase64 = “data:image/png;base64,”;
const div = document.createElement(“div”);
div.style.display = “flex”;
div.style.alignItems = “center”;
div.style.gap = “10px”; // spacing between image and QR text

                  // Create image element
                  const logoImg = document.createElement("img");
                  logoImg.src = logoBase64;
                  logoImg.style.width = "100px"; // adjust as needed
                  logoImg.style.height = "auto";
                
                  // Create span for QR Code text
                  const qrCodeSpan = document.createElement("span");
                  qrCodeSpan.innerHTML = (f.text || "").split("\n").join("<br />");
                
                  // Append image to the left, then QR code text
                  div.appendChild(logoImg);
                  div.appendChild(qrCodeSpan);
                
                  customFieldsDiv.appendChild(div);
                } 
                else {
                    const displayLabel = f.label === "Invoice Number" ? "FBR Invoice No:" : f.label || "";
                  const div = document.createElement("div");
                  div.innerHTML = `<strong>${displayLabel || ""}</strong><br />${(f.text || "").split("\n").join("<br />")}<br /><br />`;
                  customFieldsDiv.appendChild(div);
                }
			}

You need to convert the logo to Base64 string and just copy the value against logoBase64

2 Likes

Hey, Can you please help to adjust your code in my custom theme code as listed below it would be thank full for me.

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	<style>
		@page { margin: 20mm; }
		*, ::after, ::before, ::backdrop, ::file-selector-button { margin: 0; padding: 0; }
		*, ::after, ::before, ::backdrop, ::file-selector-button { box-sizing: border-box; border: 0 solid; }
		body {
			margin: 0;
			padding: 30px;
			font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
			color: #171717;
			font-size: 12px;
			line-height: 1.428571429;
			min-width: 800px;
		}
		address {
			font-style: normal;
			line-height: 1.5em;
			font-weight: bold;
			text-transform: uppercase;
		}
		dt {
			font-weight: bold;
			margin: 0 0 2px 0;
		}
		dd { margin: 0 0 16px 0; }
		dd:last-of-type { margin-bottom: 0; }
		@media print { body { padding: 0; min-width: auto; } }
		table { font-size: 12px; width: 100%; }
		tr#table-headers th {
			font-weight: bold;
			padding: 5px 10px;
			border: 1px solid #000;
			text-align: start;
			text-transform: uppercase;
		}
		tbody#table-rows td {
			padding: 5px 10px;
			text-align: start;
			vertical-align: top
		}
		tbody#table-rows tr.row td { border-left: 1px solid #000; border-right: 1px solid #000; }
		tbody#table-rows tr.last-row td { padding-bottom: 30px; border-bottom: 1px solid #000; }
		tbody#table-rows tr.column-total td {
			font-weight: bold;
			border: 1px solid #000;
			white-space: nowrap;
			text-align: right;
		}
		tbody#table-rows tr.total td { white-space: nowrap; }
		tbody#table-rows tr.total td:first-child { text-align: end; }
		tbody#table-rows tr.total td:last-child { border: 1px solid #000; text-align: right; }
		.words-in-amount { font-weight: bold; font-style: italic; font-size: 13px; margin-top: 10px; }
		.signature-block {
			display: flex; justify-content: space-between; margin-top: 50px; gap:60px;
		}
		.signature-space {
			flex: 1 0 200px;
			text-align: center;
		}
		.signature-line {
			border-top: 2px solid #000; margin: 0 auto 8px auto; width: 180px; height: 2px;
		}
		.signature-label {
			font-weight: bold; text-transform: uppercase; letter-spacing: 1px; font-size: 13px;
		}
	</style>
</head>
<body>
	<table>
		<thead>
			<tr>
				<td>
					<header style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 20px">
						<h1 id="title" style="font-size: 32px; line-height: 32px; font-weight: bold"></h1>
						<div id="business-logo" style="text-align: end"></div>
					</header>
					<section style="display: flex; margin-bottom: 20px; width: 100%; align-items: flex-start; gap: 20px">
						<address id="recipient-info" style="flex: 1"></address>
						<dl id="fields" style="flex: 1; text-align: end"></dl>
						<div aria-hidden="true" style="width: 1px; border-left: 1px solid #000; align-self: stretch "></div>
						<address id="business-info" style="white-space: nowrap"></address>
					</section>
					<p style="font-weight: bold; font-size: 14px; margin-bottom: 20px" id="description"></p>
				</td>
			</tr>
		</thead>
		<tbody>
			<tr>
				<td>
					<table style="border-collapse: collapse; width: 100%">
						<thead>
							<tr id="table-headers"></tr>
						</thead>
						<tbody id="table-rows"></tbody>
					</table>
					<script src="resources/qrcode/qrcode.js"></script>
					<div id="qrcode" style="margin-bottom: 20px"></div>
					<div id="custom-fields"></div>
					<div id="words-in-amount" class="words-in-amount"></div>
					<table><tr><td><div id="footers"></div></td></tr></table>
					<div id="status" style="text-align: center"></div>
					<!-- SIGNATURES -->
					<div class="signature-block">
						<div class="signature-space">
							<div class="signature-line"></div>
							<span class="signature-label">PREPARED BY</span>
						</div>
						<div class="signature-space">
							<div class="signature-line"></div>
							<span class="signature-label">APPROVED BY</span>
						</div>
					</div>
					<!-- END SIGNATURES -->
				</td>
			</tr>
		</tbody>
	</table>
	<script>
	// COLUMN MAPPING: Defines the columns for the table as required
	const predefinedColumns = [
		{ key: 'sr', label: 'SR', align: 'center', property: 'SR' },
		{ key: 'item', label: 'ITEM', align: 'start', property: 'Item' },
		{ key: 'qty', label: 'QTY', align: 'center', property: 'Qty' },
		{ key: 'unit_price', label: 'UNIT PRICE', align: 'right', property: 'Unit price' },
		{ key: 'amount', label: 'AMOUNT', align: 'right', property: 'Amount' },
		{ key: 'rate', label: 'RATE', align: 'center', property: 'Tax' },
		{ key: 'sales_tax', label: 'SALES TAX', align: 'right', property: 'Tax Amount' },
		{ key: 'total', label: 'TOTAL', align: 'right', property: 'Total' }
	];
	function extractTaxRate(text) {
		if(!text) return text;
		var match = text.match(/(\d+(?:\.\d+)?)%/);
		return match ? match[1] + '%' : text;
	}
	window.addEventListener("message", (event) => {
		if (event.source !== window.parent) return;
		if (event.data.type !== 'context-response') return;
		const data = event.data.body;
		document.documentElement.dir = data.direction;
		document.title = [data?.business?.name, data?.title, data?.reference].filter(Boolean).join(' - ');
		// BUSINESS INFO SECTION
		const business = data.business || {};
		var businessNameUC = (business.name || "").toUpperCase();
		var businessAddressUC = (business.address ? business.address.toUpperCase().replace(/\n/g, "<br>") : "");
		// Both business name and address in same font-weight (no <strong>)
		document.getElementById("business-info").innerHTML = `${businessNameUC}<br>${businessAddressUC}`;
		// RECIPIENT INFO SECTION
		const recipient = data.recipient || {};
		var recipientNameUC = (recipient.name || "").toUpperCase();
		var recipientAddressUC = (recipient.address ? recipient.address.toUpperCase().replace(/\n/g, "<br>") : "");
		// Both recipient name and address in same font-weight (no <strong>)
		document.getElementById("recipient-info").innerHTML = `${recipientNameUC}<br>${recipientAddressUC}`;
		document.getElementById("title").innerHTML = data.title ? data.title.toUpperCase() : "NO TITLE";
		document.getElementById("description").innerHTML = data.description ? data.description.toUpperCase() : "";
		// BUSINESS LOGO
		var businessLogoTd = document.getElementById("business-logo");
		if (business.logo) {
			const img = document.createElement("img");
			img.addEventListener("load", sendResize);
			img.src = business.logo;
			img.style = "max-height: 150px; max-width: 300px; display: inline";
			businessLogoTd.appendChild(img);
		}
		// FIELDS
		const fieldsDiv = document.getElementById("fields");
		fieldsDiv.innerHTML = "";
		(data.fields || []).forEach(f => {
			const dt = document.createElement("dt");
			dt.innerHTML = f.label.toUpperCase();
			const dd = document.createElement("dd");
			dd.innerHTML = (f.text || "").toUpperCase();
			fieldsDiv.appendChild(dt);
			fieldsDiv.appendChild(dd);
		});
		// TABLE HEADERS: Use predefinedColumns always
		const headersRow = document.getElementById("table-headers");
		headersRow.innerHTML = "";
		predefinedColumns.forEach(col => {
			const th = document.createElement("th");
			th.innerHTML = col.label;
			th.style.textAlign = col.align;
			if (col.label === 'SR' || col.label === 'QTY') { th.style.whiteSpace = 'nowrap'; th.style.width = '1px'; }
			if (col.label === 'UNIT PRICE' || col.label === 'AMOUNT' || col.label === 'TOTAL' || col.label === 'SALES TAX') th.style.whiteSpace = 'nowrap';
			headersRow.appendChild(th);
		});
		// Map Manager.io columns to new column specification
		function findColumn(property) {
			const userCols = (data.table.columns||[]);
			for (let i=0;i<userCols.length;i++) {
				if ((userCols[i].label||'').replace(/\s+/g,'').toUpperCase() === property.replace(/\s+/g,'').toUpperCase())
					return {col:userCols[i], idx:i};
			}
			return null;
		}
		// LINE ITEMS
		const rowsBody = document.getElementById("table-rows");
		rowsBody.innerHTML = "";
		(data.table.rows || []).forEach(row => {
			const tr = document.createElement("tr");
			tr.className = 'row';
			predefinedColumns.forEach(def => {
				const colFound = findColumn(def.property);
				const td = document.createElement("td");
				if (colFound) {
					let displayText = row.cells[colFound.idx]?.text || '';
					// if this is RATE, show just percent
					if (def.label === 'RATE' && displayText) displayText = extractTaxRate(displayText);
					td.innerHTML = displayText.split("\n").join("<br />");
					td.style.textAlign = def.align;
					if(['SR','QTY'].includes(def.label)) td.style.whiteSpace='nowrap';
					if(['UNIT PRICE','AMOUNT','TOTAL','SALES TAX'].includes(def.label)) td.style.whiteSpace='nowrap';
				} else {
					td.innerHTML = '';
					td.style.textAlign = def.align;
				}
				tr.appendChild(td);
			});
			rowsBody.appendChild(tr);
		});
		// Last row style
		const rows = rowsBody.querySelectorAll('tr.row');
		if (rows.length > 0) { rows[rows.length - 1].classList.add('last-row'); }
		// COLUMN TOTALS
		const trSum = document.createElement("tr");
		trSum.classList.add('column-total');
		predefinedColumns.forEach(def => {
			const colFound = findColumn(def.property);
			const td = document.createElement("td");
			td.style.textAlign = def.align;
			if (colFound && data.table.columns[colFound.idx].sumText) {
				td.innerHTML = data.table.columns[colFound.idx].sumText;
			} else {
				td.innerHTML = '';
			}
			trSum.appendChild(td);
		});
		if (trSum.innerText.trim()) rowsBody.appendChild(trSum);
		// TABLE TOTALS
		(data.table.totals || []).forEach(total => {
			const tr = document.createElement("tr");
			tr.className = 'total';
			const tdLabel = document.createElement("td");
			let labelText = total.label.toUpperCase();
			if(labelText.match(/^(SALESTAX|GST|VAT)[^\d]*\d+%?$/)) labelText = 'SALES TAX';
			tdLabel.innerHTML = labelText;
			tdLabel.colSpan = predefinedColumns.length - 1;
			const tdValue = document.createElement("td");
			tdValue.innerHTML = total.text;
			tdValue.id = total.key;
			if (total.class) tdValue.classList.add(total.class);
			tdValue.dataset.value = total.number;
			if (total.emphasis) {
				tdLabel.style.fontWeight = 'bold';
				tdValue.style.fontWeight = 'bold';
			}
			tr.appendChild(tdLabel);
			tr.appendChild(tdValue);
			rowsBody.appendChild(tr);
		});
		// Display words-in-amount just after the table
		const amountWordsDiv = document.getElementById('words-in-amount');
		amountWordsDiv.innerHTML = '';
		if(data.custom_fields && data.custom_fields.length > 0) {
			const found = data.custom_fields.find(f=>f.label && f.label.toLowerCase().includes('amount in words'));
			if(found && found.text) {
				amountWordsDiv.innerHTML = `<span>${found.text}</span>`;
			}
		}
		if(amountWordsDiv.firstElementChild) {
			amountWordsDiv.firstElementChild.style.fontWeight = 'bold';
			amountWordsDiv.firstElementChild.style.fontStyle = 'italic';
			amountWordsDiv.firstElementChild.style.textTransform = 'none';
		}
		// Custom fields ordinary display, except "amount in words" suppressed
		const customFieldsDiv = document.getElementById("custom-fields");
		customFieldsDiv.innerHTML = "";
		(data.custom_fields || []).forEach(f => {
			if (f.label && f.label.toLowerCase().includes('amount in words')) return;
			if (f.displayAtTheTop) {
				const dt = document.createElement("dt");
				dt.innerHTML = f.label.toUpperCase();
				const dd = document.createElement("dd");
				dd.innerHTML = (f.text || "").toUpperCase();
				fieldsDiv.appendChild(dt);
				fieldsDiv.appendChild(dd);
			} else {
				const div = document.createElement("div");
				div.innerHTML = `<strong>${f.label ? f.label.toUpperCase() : ""}</strong><br />${(f.text || "").split("\n").join("<br />").toUpperCase()}<br /><br />`;
				customFieldsDiv.appendChild(div);
			}
		});
		const footersDiv = document.getElementById("footers");
		footersDiv.innerHTML = "";
		(data.footers || []).forEach(f => {
			const div = document.createElement("div");
			div.style = 'margin-top: 20px';
			div.innerHTML = f;
			footersDiv.appendChild(div);
			const scripts = div.querySelectorAll("script");
			scripts.forEach(script => {
				const newScript = document.createElement("script");
				for (const attr of script.attributes) {
					newScript.setAttribute(attr.name, attr.value);
				}
				if (script.textContent) {
					newScript.textContent = script.textContent;
				}
				script.parentNode.replaceChild(newScript, script);
			});
		});
		const statusDiv = document.getElementById("status");
		if (data.emphasis?.text != null) {
			statusDiv.style.marginTop = '40px';
			const span = document.createElement("span");
			span.style = 'border-width: 5px; border-color: #FF0000; border-style: solid; padding: 10px; font-size: 20px; text-transform: uppercase';
			if (data.emphasis.positive) { span.style.color = 'green'; span.style.borderColor = 'green'; }
			if (data.emphasis.negative) { span.style.color = 'red'; span.style.borderColor = 'red'; }
			span.innerHTML = data.emphasis.text.toUpperCase();
			statusDiv.appendChild(span);
		}
		sendResize();
	}, false);
	window.addEventListener("load", () =>
		window.parent.postMessage({ type: "context-request" }, "*")
	);
	function sendResize() {
		window.parent.postMessage({
			type: "resize",
			width: document.documentElement.scrollWidth + 1,
			height: document.documentElement.scrollHeight + 1
		}, "*");
	}
	</script>
</body>
</html>

Replace the above else statement in your code with mine One. I have only posted the content of else statement. Remember to put the Base64 string value of logo in the variable in the code (const logoBase64 = “……..“

I am integrating with FBR API but getting error on Reduced tax & Exempt Goods scenarios.
“error”: “Valid Item Sr. No. is mandatory where SRO/Schedule No. is provided. Please refer to relevant reference API in the technical document for DI API for valid Item Sr. No. for the provided SRO/Schedule No.”

I am selecting SRO/Schedule NO ID and also tried Desc in payload. but every time getting same error. Can some one help in this?

Welcome to the forum @Malik25,

The error suggests that the SRO item you selected is invalid and I think this is something that you need to ask from a local Pakistani accountant.

However, it doesn’t hurt to share the rejected test invoice as some members with keen eyes could help spot the error.

Maybe we need to add some items to the dropdown list or maybe you need to change your selection.

2 Likes

Try these values in respective fields.

Tax code settings should be like below, your test should pass in case of SN027 goods at reduced rate.

2 Likes

To display the FBR Digital Invoicing Logo:

  1. Create a new Custom Theme.
  2. Locate the //CUSTOM FIELDS section and replace the code with the following:
// CUSTOM FIELDS
			// Display custom fields like notes, terms, payment instructions, etc.
			const customFieldsDiv = document.getElementById("custom-fields");
			const qrCodeDiv = document.getElementById("qrcode");
			customFieldsDiv.innerHTML = "";
			const cssBase64Image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGsAAABrCAYAAABwv3wMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RkJBNkUwMkY3NDA1MTFGMDkyNjZBM0UyNkIzNDM1OUIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RkJBNkUwMzA3NDA1MTFGMDkyNjZBM0UyNkIzNDM1OUIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGQkE2RTAyRDc0MDUxMUYwOTI2NkEzRTI2QjM0MzU5QiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGQkE2RTAyRTc0MDUxMUYwOTI2NkEzRTI2QjM0MzU5QiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PlV5w7oAAB8WSURBVHja7F0HnFTV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";
			
            const invoiceFieldKey = "24b11f97-46d7-472e-ad4b-e99fbed9197e";
            const qrFieldKey = "d2e9265a-460e-4a06-83f9-29a523a4d516";
            
            const invoiceField = (data.custom_fields || []).find(cf => cf.key === invoiceFieldKey);
            const invoiceNumber = invoiceField?.text || "";
            
            const excludedKeys = new Set([invoiceFieldKey, qrFieldKey]);
            
            (data.custom_fields || []).forEach(f => {
                if (invoiceNumber && f.key === qrFieldKey) {
                    const div = document.createElement("div");
                    div.innerHTML = `
                        <div style="background-color: #e6f2ff; border: 1px solid #b3d1ff; border-radius: 8px; padding: 10px; margin-bottom: 10px; display: inline-block;">
                            <div style="display: flex; gap: 0px; align-items: center;">
                                <img src="${cssBase64Image}" style="width: 100px; height: 100px;" />
                                ${f.text}
                            </div>
                            <div style="padding-left: 10px; padding-top: 2px; padding-bottom: 5px; text-align: left; line-height: 1.4; background-color: #fff;">
                                <strong>FBR Invoice Number:</strong><br />
                                ${invoiceNumber}
                            </div>
                        </div>
                    `;
                    qrCodeDiv.appendChild(div);
                } else if (f.displayAtTheTop && !excludedKeys.has(f.key)) {
                    const dt = document.createElement("dt");
                    dt.innerHTML = f.label;
                    const dd = document.createElement("dd");
                    dd.innerHTML = f.text;
                    fieldsDiv.appendChild(dt);
                    fieldsDiv.appendChild(dd);
                } else if (!excludedKeys.has(f.key)) {
                    const div = document.createElement("div");
                    div.innerHTML = `<strong>${f.label || ""}</strong><br />${(f.text || "").split("\n").join("<br />")}<br /><br />`;
                    customFieldsDiv.appendChild(div);
                }
            });

			// FOOTERS SECTION
  1. Modify the Invoice Number custom field:
  • Go to Settings → Custom Fields → Text Custom Fields → TextCustomField — Invoice Number — Edit
  • Check the box for “Show custom field on printed documents”

Result :

1 Like