Feature request: Add Jordan locale and implement Jordanian e-invoice QR code for sales invoices

Hello Team,

I’d like to request support for Jordan in Manager.io, specifically:

  1. Add Jordan as a supported locale
  • Country: Jordan (JO)

  • Currency: Jordanian Dinar (JOD)

  • Date format: DD/MM/YYYY (commonly used)

  • Number format: 1,234.56

  • Default tax: General Sales Tax (GST) at standard rates (please provide Jordan’s current standard and reduced rates, if applicable)

  • Localization: Arabic (ar-JO) and English (en-JO) display options for forms

  1. Implement Jordanian e-Invoice QR code on sales invoices
  • Add an optional QR code field to sales invoices that complies with Jordan’s e-invoicing requirements.

  • QR content should follow the official Jordan ZATCA-equivalent/Jordan Tax Authority specification (please use the latest published schema from the Jordan Income and Sales Tax Department). If a signed/encoded payload is required, include the appropriate fields (seller name/registration, invoice number, timestamp, tax amount, total with tax, digital signature if mandated).

  • Provide settings in Sales Invoices to enable/disable the QR code, select language, and map data fields (e.g., legal name, tax number, invoice ID, date/time, total, tax total).

  • Ensure the QR renders clearly on all built-in invoice themes/templates and in PDF.

  1. Documentation and templates
  • Include a brief guide showing how to enable the Jordan locale and QR code for sales invoices, plus any prerequisites (e.g., company tax ID format, certificate setup if signatures are required).

  • If custom email or invoice templates are needed, please provide sample templates and placement guidance for the QR.

If you need sample specifications, test payloads, or example invoices to validate against the Jordanian tax authority requirements, I can supply them. Please let me know if there is an existing roadmap item, any technical constraints, or the expected configuration steps.

Thank you!

i do the below HTML code to create tax invocie by entering data manually for seller and byer details is this ok

<!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; 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; min-width: 800px;
		}
		address { font-style: normal; line-height: 1.5em; }
		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; } #jordan-fields {display:none !important;} }
		table { font-size: 12px; width: 100%; }
		tr#table-headers th { font-weight: bold; padding: 5px 10px; border: 1px solid #000; text-align: start }
		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; }
		#jordan-fields {
			padding:10px; background:#ffe; border:1px dotted #888; margin-bottom:20px; border-radius: 7px; font-size:15px;
		}
		#jordan-fields span[contenteditable] {
			background:#fffbe8; min-width:80px; display:inline-block; border-bottom: 1px dashed #bbb; padding:2px 6px;
		}
		#jordqan-qr-code { text-align: center; margin-top: 16px; }
	</style>
</head>
<body>
<!-- الحقول اليدوية التي ستختفي عند الطباعة -->
<div id="jordan-fields">
  <b>رقمك الضريبي:</b> <span id="j_sellerTax" contenteditable="true">123456789</span>&nbsp;&nbsp;
  <b>رقم ضريبة العميل:</b> <span id="j_buyerTax" contenteditable="true">987654321</span>
  <br>
  <b>عنوانك:</b> <span id="j_sellerAddress" contenteditable="true">Amman, Jordan</span>&nbsp;&nbsp;
  <b>عنوان العميل:</b> <span id="j_buyerAddress" contenteditable="true">Irbid, Jordan</span>
  <br>
  <span style="font-size:11px; color:#b00">املأ هذه الحقول يدوياً، ولن تظهر عند الطباعة أو PDF</span>
</div>

<!-- تصميم فاتورتك كما هو (Manager.io / ديناميكي تلقائي) -->
<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>

				<!-- كود QR الأردني الجديد -->
				<div id="jordqan-qr-code"></div>
				<div style="font-size:11px; color:#888; text-align:center; margin-top:-8px; margin-bottom:8px;">
				  Jordan e-Invoice QR
				</div>

				<div id="custom-fields"></div>
				<table><tr><td><div id="footers"></div></td></tr></table>
				<div id="status" style="text-align: center"></div>
			</td>
		</tr>
	</tbody>
</table>

<script>
// برمجة قالب Manager.io الأصلي
function sendResize() {
	window.parent.postMessage({
		type: "resize",
		width: document.documentElement.scrollWidth + 1,
		height: document.documentElement.scrollHeight + 1
	}, "*");
}
window.addEventListener("message", (event) => {
	if (event.source !== window.parent) return;
	if (event.data.type !== 'context-response') return;
	const data = event.data.body;
	window.data = data; // متغير عالمي للسكربت أسفل
	document.documentElement.dir = data.direction;
	document.title = [data?.business?.name, data?.title, data?.reference].filter(Boolean).join(' - ');
	document.getElementById("title").innerHTML = data.title || "No title";
	document.getElementById("description").innerHTML = data.description || "";
	var businessLogoTd = document.getElementById("business-logo");
	if (data.business.logo) {
		const img = document.createElement("img");
		img.addEventListener("load", sendResize);
		img.src = data.business.logo;
		img.style = "max-height: 150px; max-width: 300px; display: inline";
		businessLogoTd.appendChild(img);
	}
	const business = data.business || {};
	document.getElementById("business-info").innerHTML =
		`<strong>${business.name || ""}</strong><br>${business.address ? business.address.replace(/\n/g, "<br>") : ""}`;
	const recipient = data.recipient || {};
	document.getElementById("recipient-info").innerHTML =
		`<strong>${recipient.name || ""}</strong><br>${recipient.address ? recipient.address.replace(/\n/g, "<br>") : ""}`;
	const fieldsDiv = document.getElementById("fields");
	fieldsDiv.innerHTML = "";
	(data.fields || []).forEach(f => {
		const dt = document.createElement("dt");
		dt.innerHTML = f.label;
		const dd = document.createElement("dd");
		dd.innerHTML = f.text;
		fieldsDiv.appendChild(dt);
		fieldsDiv.appendChild(dd);
	});
	const headersRow = document.getElementById("table-headers");
	headersRow.innerHTML = "";
	(data.table.columns || []).forEach(col => {
		const th = document.createElement("th");
		th.innerHTML = col.label;
		th.style.textAlign = col.align;
		if (col.minWidth) { th.style.whiteSpace = 'nowrap'; th.style.width = '1px'; }
		else if (col.nowrap) { th.style.whiteSpace = 'nowrap'; th.style.width = '80px'; }
		headersRow.appendChild(th);
	});
	const rowsBody = document.getElementById("table-rows");
	rowsBody.innerHTML = "";
	(data.table.rows || []).forEach(row => {
		const tr = document.createElement("tr");
		tr.className = 'row';
		row.cells.forEach((cell, i) => {
			var col = data.table.columns[i];
			const td = document.createElement("td");
			td.innerHTML = (cell.text || "").split("\n").join("<br />");
			td.style.textAlign = col.align;
			if (col.minWidth) { td.style.whiteSpace = 'nowrap'; td.style.width = '1px'; }
			else if (col.nowrap) { td.style.whiteSpace = 'nowrap'; td.style.width = '80px'; }
			tr.appendChild(td);
		});
		rowsBody.appendChild(tr);
	});
	const rows = rowsBody.querySelectorAll('tr.row');
	if (rows.length > 0) {
		const lastRow = rows[rows.length - 1];
		lastRow.classList.add('last-row');
	}
	const tr = document.createElement("tr");
	tr.classList.add('column-total');
	(data.table.columns || []).forEach(col => {
		const td = document.createElement("td");
		td.style.textAlign = col.align;
		td.innerHTML = col.sumText;
		tr.appendChild(td);
	});
	if (tr.innerText) rowsBody.appendChild(tr);
	(data.table.totals || []).forEach(total => {
		const tr = document.createElement("tr");
		tr.className = 'total';
		const tdLabel = document.createElement("td");
		tdLabel.innerHTML = total.label;
		tdLabel.colSpan = data.table.columns.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);
	});
	const customFieldsDiv = document.getElementById("custom-fields");
	customFieldsDiv.innerHTML = "";
	(data.custom_fields || []).forEach(f => {
		if (f.displayAtTheTop) {
			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 {
			const div = document.createElement("div");
			div.innerHTML = `<strong>${f.label || ""}</strong><br />${(f.text || "").split("\n").join("<br />")}<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;
		statusDiv.appendChild(span);
	}
	sendResize();
}, false);
window.addEventListener("load", () =>
	window.parent.postMessage({ type: "context-request" }, "*")
);

// كود QR الأردني (من الحقول اليدوية وبيانات الفاتورة)
function makeJordanQrData() {
	const data = typeof window.data !== 'undefined' ? window.data : null;
	let sellerName = data?.business?.name || '';
	let sellerTax = document.getElementById('j_sellerTax')?.innerText || '';
	let sellerAddress = document.getElementById('j_sellerAddress')?.innerText || '';
	let buyerName = data?.recipient?.name || '';
	let buyerTax = document.getElementById('j_buyerTax')?.innerText || '';
	let buyerAddress = document.getElementById('j_buyerAddress')?.innerText || '';
	let invoiceNumber = data?.reference || '';
	let issueDate = data?.timestamp ? new Date((data.timestamp - 621355968000000000) / 10000).toISOString() : '';
	let total = document.getElementById('Total')?.getAttribute('data-value') || '';
	let vatTotal = document.getElementById('VAT')?.getAttribute('data-value') || '';
	let currency = data?.currency || 'JOD';
	return {
		sellerName,sellerTaxNumber:sellerTax,sellerAddress,
		buyerName,buyerTaxNumber:buyerTax,buyerAddress,
		invoiceNumber,issueDate,total,vatTotal,currency
	}
}
function renderJordanQR() {
	const qrDiv = document.getElementById('jordqan-qr-code');
	qrDiv.innerHTML = '';
	new QRCode(qrDiv, {
		text: JSON.stringify(makeJordanQrData()),
		width: 200, height: 200,
		colorDark: "#000000",
		colorLight: "#ffffff",
		correctLevel: QRCode.CorrectLevel.L
	});
}
['j_sellerTax','j_buyerTax','j_sellerAddress','j_buyerAddress'].forEach(id=>{
	let elem = document.getElementById(id);
	if (elem) elem.addEventListener('input', renderJordanQR);
});
window.addEventListener('load', function() { setTimeout(renderJordanQR, 800); });
</script>
</body>
</html>```