@AhmedDoheen, Please share your Invoice Edit Form screen
Please show the full Edit form.
From the error you provided, it seems you may have an empty invoice line. Make sure your invoice is filled in correctly before reporting it.
It seems that you did not enter a Qty on the invoice line. Item Quantity is mandatory for e‑Invoicing.
-
I have already created a “Generate QR” button through the Extensions, and it works correctly for most users
What I don’t understand is: what exactly should I do with the Themes section?And some users either get a white screen, or the system says “QR generated” but the QR still doesn’t appear in the invoice view.
-
The second point (less important now):
I currently have to run this manual “Generate QR Code” action for all old invoices, which is time-consuming
Use Custom Theme to display QR Code Phase II
Moving forward, I will use the Image Custom Field to store the QR Code image. This way, the QR Code will be displayed automatically without relying on custom Themes.
I am not using any custom themes.
Use custom themes for now, while waiting for the QR code text to be converted into an image.
I’ve already asked the developer to help convert the QR code text into an image and store it in the QR code image custom field.
Hello,
thank you for all the great work @Mabaega
we are using custom sales invoice template
and soon need to be integrated with ZATCA
can we use this Extension with our custom template
This is our template and the QR in our template
Thank you.
<!DOCTYPE html>
<html lang="ar">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>فاتورة مبيعات ضريبية / Taxable Sales Invoice</title>
<link href="https://fonts.googleapis.com/css2?family=Cairo:wght@400;700&family=Roboto:wght@400;700&display=swap" rel="stylesheet">
<style>
:root { --page-padding: 14mm; --max-width: 880px; --muted: #f4f4f4; }
*{ box-sizing: border-box; }
body {
font-family: 'Cairo', 'Arial', sans-serif;
margin: 0; padding: 0; background-color: var(--muted);
direction: rtl; -webkit-text-size-adjust: 100%; color: #222;
}
.invoice-container {
margin: 3mm auto; padding: 8px; max-width: var(--max-width);
background-color: #fff; box-shadow: 0 0 8px rgba(0,0,0,0.04);
border-radius: 4px; break-inside: avoid; page-break-inside: avoid;
}
/* Header */
header { display:flex; justify-content:space-between; align-items:center; gap:15px;
border-bottom:2px solid #000; padding:10px 15px; margin-bottom:12px; background:#f8f9fa; box-shadow:0 2px 5px rgba(0,0,0,0.08); }
.header-section { flex:1 1 32%; min-width:0; font-size:10px; line-height:1.4; }
.header-section strong { font-size:13px; color:#1a5490; font-weight:700; }
.header-center { text-align:center; padding:0 10px; border-left:2px solid #ddd; border-right:2px solid #ddd; }
.header-center img { max-height:75px; width:auto; max-width:100%; }
.header-right { text-align:right; direction:rtl; color:#444; }
.header-left { text-align:left; direction:ltr; font-family:'Roboto', sans-serif; color:#444; }
/* Title */
.main-title { display:flex; justify-content:space-between; align-items:center; background:#f0f0f0;
padding:6px 12px; font-weight:600; margin:15px auto; border-radius:3px; font-size:16px; }
.main-title .ar{ text-align:right; flex:1; } .main-title .en{ text-align:left; flex:1; }
/* Tables (عام) */
.tg{ border-collapse:collapse; width:98%; margin:4px auto; table-layout:auto; word-wrap:break-word; }
.tg th, .tg td{ border:2px solid #000; padding:4px 8px; font-size:11px; text-align:center; vertical-align:middle; line-height:1.3; }
.tg th{ background:#D0D0D0; color:#000; font-weight:700; font-size:12px; }
.tg-title{ background:#1a5490; color:#fff; font-weight:700; padding:8px; font-size:13px; }
/* تخصيص جداول المعلومات فقط (حتى لا تؤثر على جدول الأصناف) */
.info-table td:first-child, .info-table td:last-child{
width:28%;
background:#d0d0d0;
font-weight:600;
}
.info-table td:nth-child(2){ width:auto; text-align:center; background:#fff; }
/* Print */
@page { size:A4; margin:12mm; }
@media print{
html,body{ height:100%; background:#fff; }
.invoice-container{ box-shadow:none; margin:0; padding:12mm; border-radius:0; page-break-after:always; }
.invoice-container:last-of-type{ page-break-after:auto; }
img{ max-height:90px; }
}
.summary-label{ text-align:right; font-weight:700; }
.grand-total{ background:#f1f1f1; font-weight:800; }
.items-table .description{ text-align:right; }
</style>
</head>
<body>
<!-- ===== PAGE 1: Supplier + Customer ===== -->
<div class="invoice-container">
<header>
<div class="header-section header-right"><p class="ar"><strong>{{ business.name }}</strong><br></p></div>
<div class="header-section header-center"><img src="{{ business.logo }}" alt="Logo"></div>
<div class="header-section header-left"><p class="en"><strong>{{ business.name }}</strong><br></p></div>
</header>
<div class="main-title">
<span class="ar">فاتورة مبيعات ضريبية</span>
<span class="en">Taxable Sales Invoice</span>
</div>
<!-- Supplier Info -->
<table class="tg info-table">
<thead><tr><th colspan="3" class="tg-title"><span class="ar">معلومات المورد</span><span class="en">Supplier Information</span></th></tr></thead>
<tbody>
<tr><td>اسم المورد</td><td>{{ business.name }}</td><td>Supplier Name</td></tr>
<tr><td>الرقم الموحد</td><td id="supplier-unified-number"></td><td>Unified Number</td></tr>
<tr><td>رقم السجل التجاري</td><td id="supplier-companyCR"></td><td>C.R Number</td></tr>
<tr><td>الرقم الضريبي</td><td id="supplier-VAT-Number"></td><td>VAT Number</td></tr>
<tr><td>الهاتف</td><td id="supplier-Phone-Number"></td><td>Phone</td></tr>
<tr><td>البريد الالكتروني</td><td id="supplier-Email"></td><td>Email</td></tr>
<tr><td>الموقع الالكتروني</td><td id="supplier-WebSite"></td><td>Website</td></tr>
<tr><td>العنوان المختصر</td><td id="supplier-Short-Address"></td><td>Short Address</td></tr>
<tr><td>رقم المبنى</td><td id="supplier-Building-Number"></td><td>Building Number</td></tr>
<tr><td>الشارع</td><td id="supplier-Street-Name"></td><td>Street</td></tr>
<tr><td>الحي</td><td id="supplier-District-Name"></td><td>District</td></tr>
<tr><td>رقم الفرعي</td><td id="supplier-Sub-Number"></td><td>Sub Number</td></tr>
<tr><td>الرمز البريدي</td><td id="supplier-Postal-Code"></td><td>Postal Code</td></tr>
<tr><td>المدينة</td><td id="supplier-City"></td><td>City</td></tr>
<tr><td>الدولة</td><td id="supplier-country"></td><td>Country</td></tr>
</tbody>
</table>
<br>
<!-- Customer Info -->
<table class="tg info-table">
<thead><tr><th colspan="3" class="tg-title"><span class="ar">معلومات العميل</span><span class="en">Customer Information</span></th></tr></thead>
<tbody>
<tr><td>اسم العميل</td><td>{{ recipient.name }}</td><td>Customer Name</td></tr>
<tr><td>الرقم الضريبي</td><td id="recipient-VAT-Number"></td><td>VAT Number</td></tr>
<tr><td>العنوان المختصر</td><td id="recipient-Short-Address"></td><td>Short Address</td></tr>
<tr><td>رقم المبنى</td><td id="recipient-Building-Number"></td><td>Building Number</td></tr>
<tr><td>الشارع</td><td id="recipient-Street-Name"></td><td>Street</td></tr>
<tr><td>الحي</td><td id="recipient-District-Name"></td><td>District</td></tr>
<tr><td>رقم الفرعي</td><td id="recipient-Sub-Number"></td><td>Sub Number</td></tr>
<tr><td>الرمز البريدي</td><td id="recipient-Postal-Code"></td><td>Postal Code</td></tr>
<tr><td>المدينة</td><td id="recipient-City"></td><td>City</td></tr>
<tr><td>الدولة</td><td id="recipient-country"></td><td>Country</td></tr>
<tr><td>الهاتف</td><td id="recipient-Phone-Number"></td><td>Phone</td></tr>
</tbody>
</table>
</div>
<!-- ===== PAGE 2: Invoice Info + Items + (Right text + Left QR) ===== -->
<div class="invoice-container">
<header>
<div class="header-section header-right"><p class="ar"><strong>{{ business.name }}</strong><br></p></div>
<div class="header-section header-center"><img src="{{ business.logo }}" alt="Logo"></div>
<div class="header-section header-left"><p class="en"><strong>{{ business.name }}</strong><br></p></div>
</header>
<div class="main-title">
<span class="ar">فاتورة مبيعات ضريبية</span>
<span class="en">Taxable Sales Invoice</span>
</div>
<!-- Invoice Info -->
<table class="tg info-table">
<thead>
<tr><th colspan="3" class="tg-title"><span class="ar">معلومات الفاتورة</span><span class="en">Invoice Information</span></th></tr>
</thead>
<tbody>
<tr><td>رقم الفاتورة</td><td>{{ reference }}</td><td>Invoice Number</td></tr>
<tr><td>تاريخ الفاتورة</td><td id="invoice-date"></td><td>Invoice Date</td></tr>
<tr><td>تاريخ التوريد</td><td id="delivery-date"></td><td>Delivery Date</td></tr>
<tr><td>رقم أمر الشراء</td><td id="po-number"></td><td>PO Number</td></tr>
<tr><td>رقم عرض السعر</td><td id="quotation-number"></td><td>Quotation Number</td></tr>
<tr><td>اسم البائع</td><td id="sales-name"></td><td>Sales Name</td></tr>
<tr><td>العملة</td><td id="currency-symbol"></td><td>Currency</td></tr>
</tbody>
</table>
<!-- Items Table -->
<table class="tg items-table" id="items-table" style="margin-top:8px;">
<colgroup>
<col width="2%">
<col width="60%">
<col width="17%">
<col width="2%">
<col width="5%">
<col width="2%">
<col width="5%">
<col width="2%">
<col width="5%">
</colgroup>
<thead>
<tr>
<th>No</th>
<th>اسم الصنف / Item Name</th>
<th>البيان / Description</th>
<th>الكمية / Qty</th>
<th>سعر الوحدة / Unit Price</th>
<th>الخصم / Discount</th>
<th>المجموع قبل الضريبة / Total excl. VAT</th>
<th>مبلغ الضريبة / Tax Amount</th>
<th>الإجمالي شامل الضريبة / Total incl. VAT</th>
</tr>
</thead>
<tbody id="items-body"></tbody>
<tbody id="items-totals"></tbody>
</table>
<!-- Right text (بيان + ملاحظات) + Left QR -->
<table style="width:98%; margin:10px auto 0 auto;">
<tr>
<!-- Right side: text -->
<td style="text-align:right; vertical-align:top; width:70%; direction:rtl;">
<div id="invoice-extra" style="font-size:12px; line-height:1.6;">
<div><strong>بيان الفاتورة:</strong> <span id="invoice-description"></span></div>
<div><strong>ملاحظات فاتورة المبيعات:</strong> <span id="invoice-notes"></span></div>
</div>
</td>
<!-- Left side: QR -->
<td style="text-align:left; vertical-align:top; width:30%;">
<div id="zatca-qr" style="margin:10px 0; text-align:left; direction:ltr;">
<div id="qrcode"></div>
</div>
</td>
</tr>
</table>
</div> <!-- /invoice-container (page 2) -->
<!-- ===== PAGE 3: Bank + Notes ===== -->
<div class="invoice-container">
<header>
<div class="header-section header-right">
<p class="ar"><strong>{{ business.name }}</strong><br></p>
</div>
<div class="header-section header-center">
<img src="{{ business.logo }}" alt="Logo">
</div>
<div class="header-section header-left">
<p class="en"><strong>{{ business.name }}</strong><br></p>
</div>
</header>
<!-- Bank Info -->
<br><br>
<table class="tg info-table">
<thead>
<tr>
<th colspan="3" class="tg-title">
<div class="title-flex">
<span class="ar">معلومات الحساب البنكي</span>
<span class="en">Bank Account Information</span>
</div>
</th>
</tr>
</thead>
<tbody>
<tr><td>اسم البنك</td><td><span id="bank-name"></span></td><td>Bank Name</td></tr>
<tr><td>اسم المستفيد</td><td><span id="beneficiary-name"></span></td><td>Beneficiary Name</td></tr>
<tr><td>رقم الحساب</td><td><span id="account-number"></span></td><td>Account Number</td></tr>
<tr><td>الآيبان</td><td><span id="iban"></span></td><td>IBAN</td></tr>
<tr><td>عنوان الفرع</td><td><span id="branch-address"></span></td><td>Branch Address</td></tr>
<tr><td>رقم السويفت</td><td><span id="swift-code"></span></td><td>Swift Code</td></tr>
</tbody>
</table>
<!-- Notes -->
<div class="notes" style="margin-top:12px; line-height:1.7; font-size:12px;">
<strong>ملاحظات عامة / General Notes:</strong><br><br>
<!-- النص العربي -->
<div style="direction:rtl; text-align:right;">
<strong>ملاحظات عامة :</strong><br>
1- يرجى قراءة والاطلاع على سياسة وشروط البيع والضمان.<br>
</div>
<!-- النص الإنجليزي -->
<div style="direction:ltr; text-align:left;">
<strong>General Note:</strong><br>
1- Please read and understand our Terms of Sale, Warranty <br>
website.
</div>
</div>
<footer style="margin-top:14px; text-align:center; font-size:12px;">
<span class="en">Thank you for your business!</span> /
<span class="ar">شكراً لتعاملكم معنا!</span>
</footer>
</div>
<!-- QR library -->
<script src="resources/qrcode/qrcode.js"></script>
<script>
/* ===== Helpers ===== */
function setText(id, value){ var el=document.getElementById(id); if(el) el.textContent = value ?? ""; }
function getCustomByKey(entity, key){
var list=(entity && entity.custom_fields) || [];
var hit = list.find(function(f){ return f.key === key; });
return hit ? (fText(hit)) : "";
function fText(f){ return (f && (f.text || f.value || "") ) || ""; }
}
function getFieldValue(data, keys){
if(!data) return '';
const fields = data.fields || [];
const customs = data.custom_fields || [];
for(const k of keys){ if(data[k]) return data[k]; }
for(const k of keys){
const f = fields.find(ff =>
(ff.label||'').toLowerCase().includes(k.toLowerCase()) ||
(ff.key||'').toLowerCase().includes(k.toLowerCase())
);
if (f) return f.text || '';
}
for(const k of keys){
const c = customs.find(cf =>
(cf.label||'').toLowerCase().includes(k.toLowerCase()) ||
(cf.key||'').toLowerCase().includes(k.toLowerCase())
);
if (c) return c.text || '';
}
return '';
}
function parseNumberLike(s){
if (s == null) return NaN;
if (typeof s === 'number') return s;
s = (''+s).trim();
if (!s) return NaN;
var a = s.replace(/[^\d\-,.]/g,'');
var try1 = parseFloat(a.replace(/,/g,''));
if (!isNaN(try1)) return try1;
var try2 = parseFloat(a.replace(/\./g,'').replace(',', '.'));
return isNaN(try2) ? NaN : try2;
}
function fmt(n){
if (!isFinite(n)) return "0.00";
return n.toLocaleString('en-US', {minimumFractionDigits:2, maximumFractionDigits:2});
}
function stripRiyal(s){
return (s||"").replace(/\s*ريال(?:\s*سعودي)?/g,'').trim();
}
function sendResize(){
window.parent.postMessage({ type:"resize",
width:document.documentElement.scrollWidth+1,
height:document.documentElement.scrollHeight+1 }, "*");
}
// ====== جدول الأصناف + المجاميع ======
function populateItemsTable(data) {
var cols = (data.table && data.table.columns) || [];
var rows = (data.table && data.table.rows) || [];
var totals = (data.table && data.table.totals) || [];
var body = document.getElementById("items-body");
var totalsBody = document.getElementById("items-totals");
if (!body) return;
body.innerHTML = "";
if (totalsBody) totalsBody.innerHTML = "";
function findCol(patterns) {
return cols.findIndex(function(c){
var L = (c.label || "").toLowerCase().trim();
return patterns.some(function(p){ return p.test(L); });
});
}
function cellText(row, idx){
return (idx >= 0 && row.cells[idx] && (row.cells[idx].text || "").trim()) || "";
}
// اكتشاف الأعمدة
var noIdx = findCol([/^\s*(no\.?|#|line|sequence)\s*$/, /ترقيم/, /رقم\s*السطر/]);
var nameIdx = findCol([/item\s*name/, /^name$/, /اسم\s*الصنف/, /^الصنف$/, /^\s*البند\s*$/]);
var descIdx = findCol([/description/, /الوصف/, /details/, /البيان/]);
var qtyIdx = findCol([/qty/, /quantity/, /الكمية/]);
var unitIdx = findCol([/unit\s*price/, /price\s*each/, /سعر\s*الوحدة/, /سعر\s*الحبة/]);
var discIdx = findCol([/discount/, /خصم/]);
var taxAmountIdx = findCol([/tax\s*amount/i, /vat\s*amount/i, /amount\s*of\s*tax/i, /مبلغ\s*الضريبة/i, /قيمة\s*الضريبة/i]);
var taxRateIdx = findCol([/tax\s*rate/i, /vat\s*rate/i, /نسبة\s*الضريبة/i, /%/]);
var taxGenericIdx= findCol([/\btax\b/i, /\bvat\b/i, /ضريبة/]);
var totalIdx = findCol([/total/, /الإجمالي/, /المجموع/]);
// مجاميع
var sumBase = 0, sumDisc = 0, sumBeforeTax = 0, sumTax = 0, sumGrand = 0;
rows.forEach(function(row, i){
var tr = document.createElement("tr");
// ترقيم
var tdNo = document.createElement("td");
tdNo.textContent = cellText(row, noIdx) || String(i + 1);
tr.appendChild(tdNo);
// اسم الصنف + الوصف
var descRaw = cellText(row, descIdx);
var nameText = cellText(row, nameIdx);
var descText = descRaw;
if (!nameText && descRaw) {
var lines = descRaw.split(/\r?\n/).map(function(s){ return s.trim(); }).filter(Boolean);
if (lines.length > 1) { nameText = lines[0]; descText = lines.slice(1).join('\n'); }
else {
var dash = descRaw.split(/\s[-–]\s/);
if (dash.length > 1) { nameText = dash[0].trim(); descText = dash.slice(1).join(' - ').trim(); }
}
}
var tdName = document.createElement("td"); tdName.textContent = nameText || ""; tr.appendChild(tdName);
var tdDesc = document.createElement("td"); tdDesc.className = "description"; tdDesc.textContent = descText || ""; tr.appendChild(tdDesc);
// الكمية + السعر
var qtyText = cellText(row, qtyIdx);
var unitText = cellText(row, unitIdx);
var qtyNum = parseNumberLike(qtyText);
var unitNum = parseNumberLike(unitText);
var baseNum = (isFinite(qtyNum) && isFinite(unitNum)) ? (qtyNum * unitNum) : 0;
var tdQty = document.createElement("td"); tdQty.textContent = qtyText; tr.appendChild(tdQty);
var tdUnit = document.createElement("td"); tdUnit.textContent = unitText; tr.appendChild(tdUnit);
// الخصم
var discText = cellText(row, discIdx);
var discNum = parseNumberLike(discText);
if (!isFinite(discNum)) discNum = 0;
var tdDisc = document.createElement("td");
tdDisc.textContent = discText ? discText : "0";
tr.appendChild(tdDisc);
// المجموع قبل الضريبة
var beforeTaxNum = baseNum - discNum;
var tdBef = document.createElement("td");
tdBef.textContent = fmt(beforeTaxNum);
tr.appendChild(tdBef);
// الضريبة
var taxText = "";
var taxNum = NaN;
if (taxAmountIdx >= 0) {
taxText = cellText(row, taxAmountIdx);
taxNum = parseNumberLike(taxText);
} else {
var rateText = (taxRateIdx >= 0 ? cellText(row, taxRateIdx) : cellText(row, taxGenericIdx));
if (rateText && /%/.test(rateText)) {
var rate = parseFloat(rateText.replace('%','')) / 100;
if (isFinite(rate)) { taxNum = beforeTaxNum * rate; taxText = fmt(taxNum); }
} else if (rateText && isFinite(parseNumberLike(rateText)) && parseNumberLike(rateText) < 1 && parseNumberLike(rateText) > 0) {
var r = parseNumberLike(rateText);
if (isFinite(r)) { taxNum = beforeTaxNum * r; taxText = fmt(taxNum); }
} else {
var totalTextTmp = cellText(row, totalIdx);
var totalNumTmp = parseNumberLike(totalTextTmp);
if (isFinite(totalNumTmp)) {
taxNum = totalNumTmp - beforeTaxNum;
if (Math.abs(taxNum) > 1e-9) taxText = fmt(taxNum);
}
}
}
if (!taxText) taxText = "0";
var tdTax = document.createElement("td"); tdTax.textContent = taxText; tr.appendChild(tdTax);
// الإجمالي شامل الضريبة
var totalText = cellText(row, totalIdx);
var totalNum = parseNumberLike(totalText);
if (!isFinite(totalNum)) {
totalNum = beforeTaxNum + (isFinite(taxNum) ? taxNum : parseNumberLike(taxText) || 0);
totalText = fmt(totalNum);
}
var tdTotal = document.createElement("td"); tdTotal.textContent = totalText || fmt(totalNum); tr.appendChild(tdTotal);
body.appendChild(tr);
// تجميع
sumBase += baseNum;
sumDisc += discNum;
sumBeforeTax += beforeTaxNum;
var taxNumAgg = parseNumberLike(taxText);
if (isFinite(taxNumAgg)) sumTax += taxNumAgg;
if (isFinite(totalNum)) sumGrand += totalNum;
});
// Totals من Manager.io
function findTotalByKeyOrLabel(keysRegex, labelsRegex) {
return totals.find(function(t){
var k = (t.key || "").toLowerCase();
var l = (t.label || "").toLowerCase();
return (keysRegex && keysRegex.test(k)) || (labelsRegex && labelsRegex.test(l));
});
}
var tSubtotal = findTotalByKeyOrLabel(/subtotal/, /subtotal|المجموع\s*غير\s*شامل/i);
var tDiscount = findTotalByKeyOrLabel(/discount/, /discount|خصم/i);
var tTax = findTotalByKeyOrLabel(/tax|vat/, /vat|tax|ضريبة/i);
var tGrand = findTotalByKeyOrLabel(/total/, /total|الإجمالي\s*النهائي|المجموع\s*النهائي/i);
function addTotalRow(labelArEn, valueHtml, isGrand){
var totalsBody = document.getElementById("items-totals");
if (!totalsBody) return;
var tr = document.createElement("tr");
var tdLabel = document.createElement("td");
var tdVal = document.createElement("td");
tdLabel.colSpan = 8; /* 9 أعمدة: 8 لليبل + 1 للقيمة */
tdLabel.className = "summary-label" + (isGrand ? " grand-total" : "");
tdLabel.textContent = labelArEn;
tdVal.innerHTML = valueHtml || "0";
tr.appendChild(tdLabel);
tr.appendChild(tdVal);
totalsBody.appendChild(tr);
}
// 1) الإجمالي (قبل الخصم)
var totalBaseText = tSubtotal ? (tSubtotal.text || fmt(sumBase)) : fmt(sumBase);
addTotalRow("الإجمالي / Total", totalBaseText, false);
// 2) الخصم (إظهار فقط إذا > 0)
var discNumberFromTotals = (tDiscount && isFinite(Number(tDiscount.number))) ? Number(tDiscount.number) : 0;
var discountToShow = discNumberFromTotals || sumDisc;
if (discountToShow > 0) {
addTotalRow("الخصم / Discount", tDiscount ? (tDiscount.text || fmt(discountToShow)) : fmt(discountToShow), false);
}
// 3) الإجمالي بعد الخصم
var afterDiscountNumber = parseNumberLike(stripRiyal(totalBaseText)) - discountToShow;
addTotalRow("الإجمالي بعد الخصم / Total after Discount", fmt(afterDiscountNumber), false);
// 4) الضريبة
var taxTextFinal = tTax ? (tTax.text || fmt(sumTax)) : fmt(sumTax);
taxTextFinal = stripRiyal(taxTextFinal);
addTotalRow("الضريبة / VAT", taxTextFinal, false);
// 5) الإجمالي النهائي شامل الضريبة
var grandTextFinal = tGrand ? (tGrand.text || fmt(afterDiscountNumber + sumTax)) : fmt(afterDiscountNumber + sumTax);
grandTextFinal = stripRiyal(grandTextFinal);
addTotalRow("الإجمالي النهائي شامل الضريبة / Total incl. VAT", grandTextFinal, true);
}
/* ===== ZATCA helpers ===== */
function generateZatcaTLVBase64(data){
function appendTLV(tag, text, byteList){
const enc = new TextEncoder().encode(String(text ?? ''));
byteList.push(tag);
byteList.push(enc.length);
for (let b of enc) byteList.push(b);
}
const b = data.business || {};
const totals = (data.table && data.table.totals) || [];
const businessName = b.name || 'No name';
const vatNumber = ((b.custom_fields || []).find(f => f.key === '360acdc6-2784-4913-9b91-ad9247971ffd')?.text) || '0000000000000';
const isoTs = new Date((data.timestamp - 621355968000000000) / 10000).toISOString();
let total = 0;
let vat = 0;
for (const t of totals){
const k = (t.key || '').toLowerCase();
if (k === 'total' && isFinite(Number(t.number))){
total = Number(t.number);
}
}
for (const t of totals){
if (t.class === 'taxAmount' && isFinite(Number(t.number))){
vat += Number(t.number);
} else {
const lbl = (t.label || '').toLowerCase();
if (/(vat|tax|ضريبة)/i.test(lbl) && isFinite(Number(t.number))){
vat += Number(t.number);
}
}
}
const bytes = [];
appendTLV(1, businessName, bytes);
appendTLV(2, vatNumber, bytes);
appendTLV(3, isoTs, bytes);
appendTLV(4, (total || 0).toFixed(2), bytes);
appendTLV(5, (vat || 0).toFixed(2), bytes);
const tlv = Uint8Array.from(bytes);
return btoa(String.fromCharCode(...tlv));
}
function renderZatcaQR(base64, elementId){
const el = document.getElementById(elementId);
if (!el || !base64) return;
el.innerHTML = '';
new QRCode(el, {
text: base64,
width: 160,
height: 160,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.L
});
}
/* ===== Fill on message ===== */
window.addEventListener("message", function (event) {
if (event.source !== window.parent) return;
if (event.data.type !== "context-response") return;
var data = event.data.body || {};
var b = data.business || {};
/* Supplier (business.custom_fields) */
setText("supplier-unified-number", getCustomByKey(b, "2cbd4eba-07c2-49f8-b473-XXXXXXXXXXXX"));
setText("supplier-companyCR", getCustomByKey(b, "8aee5a4b-21a5-47c9-97d7-XXXXXXXXXXXX"));
setText("supplier-VAT-Number", getCustomByKey(b, "360acdc6-2784-4913-9b91-XXXXXXXXXXXX"));
setText("supplier-Phone-Number", getCustomByKey(b, "a0ec477e-556d-4cbb-84b1-XXXXXXXXXXXX"));
setText("supplier-Email", getCustomByKey(b, "529530a3-0db7-4903-8d92-XXXXXXXXXXXX"));
setText("supplier-WebSite", getCustomByKey(b, "7824c3cd-34ee-40a8-bad7-XXXXXXXXXXXX"));
setText("supplier-Short-Address", getCustomByKey(b, "dc1df740-9907-4567-8a28-XXXXXXXXXXXX"));
setText("supplier-Building-Number", getCustomByKey(b, "2dd946df-6281-4645-8b8d-XXXXXXXXXXXX"));
setText("supplier-Street-Name", getCustomByKey(b, "8029179e-dd29-405d-890c-XXXXXXXXXXXX"));
setText("supplier-District-Name", getCustomByKey(b, "328481cf-cdf9-46f7-8819-XXXXXXXXXXXX"));
setText("supplier-Sub-Number", getCustomByKey(b, "bee87bb5-eec2-4275-9a3b-XXXXXXXXXXXX"));
setText("supplier-Postal-Code", getCustomByKey(b, "cba616e1-c330-43ed-9b1a-XXXXXXXXXXXX"));
setText("supplier-City", getCustomByKey(b, "f5851a40-2032-4691-91be-XXXXXXXXXXXX"));
setText("supplier-country", getCustomByKey(b, "7b80775e-5031-464c-8041-XXXXXXXXXXXX"));
/* Customer from recipient.address lines */
var addrRaw = (data.recipient && data.recipient.address) || "";
var lines = addrRaw.split(/\r?\n/).map(s=>s.trim()).filter(Boolean);
function cleanLabel(v){ return v.replace(/^([^::]+)[::]\s*/, '').trim(); }
setText("recipient-VAT-Number", cleanLabel(lines[0] || "")); // الرقم الضريبي
setText("recipient-Short-Address", cleanLabel(lines[1] || "")); // العنوان المختصر
setText("recipient-Building-Number",cleanLabel(lines[2] || "")); // رقم المبنى
setText("recipient-Street-Name", cleanLabel(lines[3] || "")); // الشارع
setText("recipient-District-Name", cleanLabel(lines[4] || "")); // الحي
setText("recipient-Sub-Number", cleanLabel(lines[5] || "")); // الرقم الفرعي
setText("recipient-Postal-Code", cleanLabel(lines[6] || "")); // الرمز البريدي
setText("recipient-City", cleanLabel(lines[7] || "")); // المدينة
setText("recipient-country", cleanLabel(lines[8] || "")); // الدولة
setText("recipient-Phone-Number", cleanLabel(lines[9] || "")); // الهاتف
/* Invoice Date + Delivery Date + PO/Quotation/Sales + Currency */
var invDate = getFieldValue(data, ["date","invoice date","تاريخ الفاتورة"]);
setText("invoice-date", invDate);
var supplyFromKey = getCustomByKey(data, "c0f06e53-aec2-44a7-95d6-XXXXXXXXXXXX");
var supplyFallback = getFieldValue(data, ["delivery date","supply date","تاريخ التوريد","تاريخ التسليم"]);
setText("delivery-date", supplyFromKey || supplyFallback || "");
setText("po-number", getCustomByKey(data, "c26cf028-68c3-4eab-8e3c-XXXXXXXXXXXX"));
setText("quotation-number", getCustomByKey(data, "0e4d4223-b812-4f78-96f4-XXXXXXXXXXXX"));
setText("sales-name", getCustomByKey(data, "4a036dd7-b47a-4189-aad7-XXXXXXXXXXXX"));
setText("currency-symbol", "ريال سعودي - SAR");
// جدول الأصناف + المجاميع
populateItemsTable(data);
// بيان الفاتورة + ملاحظات فاتورة المبيعات
setText("invoice-description", data.description || "");
setText("invoice-notes", getCustomByKey(data, "b6da8131-ac1a-4564-8ebd-XXXXXXXXXXXX"));
// ZATCA QR
try{
var base64FromCustom = '';
(data.custom_fields || []).forEach(function(f){
if (f.label === 'Base64 QRCode' && f.text && f.text.length > 10){
base64FromCustom = f.text;
}
});
var qrBase64 = base64FromCustom || generateZatcaTLVBase64(data);
renderZatcaQR(qrBase64, 'qrcode');
}catch(e){
console.error('ZATCA QR error:', e);
}
// ===== Bank custom fields (from Business) for PAGE 3 =====
setText("bank-name", getCustomByKey(b, "0b8b1f4c-e19d-4d9a-a117-XXXXXXXXXXXX"));
setText("beneficiary-name", getCustomByKey(b, "13b029a0-f6bf-4593-8560-XXXXXXXXXXXX"));
setText("account-number", getCustomByKey(b, "480f0493-4d01-4716-bf0c-XXXXXXXXXXXX"));
setText("iban", getCustomByKey(b, "04a0b64a-7877-4d7d-bd05-XXXXXXXXXXXX"));
setText("branch-address", getCustomByKey(b, "bdaeb869-e685-40d1-9c3d-XXXXXXXXXXXX"));
setText("swift-code", getCustomByKey(b, "819b27a9-bb06-4b2d-b78b-XXXXXXXXXXXX"));
sendResize();
}, false);
window.addEventListener("load", function(){
window.parent.postMessage({ type:"context-request" }, "*");
});
</script>
</body>
</html>
There should be no issues with the theme you’re using; only the QR Code display section for Phase II may require adjustments.
Hello @Mabaega
Thank you for the support,
is there any short code that I can insert into our template that will display the Phase 2 QR in our template
Another thing we are getting ready for the ZATCA integration, should we use this
this Extension or ZatcaEGS Extension.
thank you ![]()
@Hazem Just try it…
You need to test all types of your invoices in a simulation environment (using a copy/backup of your business data).
If everything works well, you are ready to proceed with ZATCA Phase II integration using your real business data in the Production Environment.
Your themes are already able to display the Phase II QR code correctly.
Announcement: Zatca Extension Update - v25.11.22.0003
The Zatca Extension has been updated with a new feature that allows you to batch update all QR codes, covering both Phase I and Phase II invoices.
Critical Warning – Read Before Proceeding
This operation will update QR codes for multiple invoices at once.
- Phase I Invoices: New QR codes will be generated based on the current invoice data.
- Phase II Invoices (CLEARED/REPORTED): QR codes will be replaced with stored ZATCA‑approved codes.
Before you start:
- Create a backup of your Business database.
- Test on a small batch (5–10 invoices) first.
- Verify the results after processing all invoices.
- Stop immediately if you notice any errors.
Installation & Usage
- Create a new extension (see example image below).
- Go to Settings → Business Details.
- Click the Extension button Batch Update QRCode.
- A form will appear. Select Sales Invoice or Credit Note, then press Start.
- All logs will be displayed during the process.
After everything is updated as you want, you can remove this extension from your business data to prevent repeated updates.
Notes:
- For Phase I QR codes, the timestamp will be created according to the invoice date, while the time will follow the moment you perform the QR code update.
- If you have other ideas or suggestions, please feel free to share them.
i used to create this invoice from another laptop,
once i have tried from my first computer it worked fine
it’s solved now thank you.
there’s a small issue, I have 80 sales invoices and this extension only updates 50 invoices, the remaining 30 invoices remain without phase 1 qr codes.
Any solutions?
Try using smaller batches, 5–10 per batch.
Fixed in version 25.12.07.0001
Hi Dear,
we are using Server Edition version 25.11.9.3101 and we have many users with Restricted user Role
once we as Administrator access it from our user ZATCA e-Invoice Phase II Extension working fine for us (below the sample)
but if our employees access from there are users ZATCA e-Invoice Phase II Extension not working and goes to ZATCA eInvoice Phase I (below sample)
and if we change they are role as Administrator works perfectly with them, so kindly advice to solve this issue.














