⚠️ Issue with ZATCA Integration in Manager.io

Dear Support Team,



I am currently facing an issue while trying to integrate Manager.io with the ZATCA e-invoicing platform using the ZatcaEGS plugin (Phase 2 – Integration Phase).

When I click on the “Get CCSID” button, I receive the following error message:

Error: Error getting CCSID: Bad Request

I have already verified the following:

  • All Business Info fields are filled in correctly, including the company name, VAT number, and CR number.
  • The company name and other details exactly match the registered data with ZATCA.
  • The device’s time and date settings are correct and set to (UTC+03:00) Riyadh.
  • The CSR Environment Type is set to “Production”.
  • A modern browser is being used without any VPN or script-blocking extensions.

Please note that I had previously completed the integration successfully. However, after encountering an issue within the Manager software, I disconnected the device and attempted to re-integrate it. Since then, this error has been appearing.

I’ve attached a screenshot of the current setup for your reference.

Kindly assist in identifying the root cause of this error and advise me on how to proceed to restore the integration

Onboarding failure due to Zatca changing the eInvoice rule.

“Charge on price level (BG-29) is not allowed”

This causes a failure in the Compliance Check to obtain the Production CSID.

I will try to make the adjustments as soon as possible.

If you previously reset the Business Detail, the best way is to restore your business data.

1 Like

I restored a backup, but the issue was not resolved, and this message still appears."

Please assist me urgently, as I need to issue invoices and complete the integration with ZATCA before the end of the month.

@Mabaega

I was trying to integrate one customer but I got below error

Error:

Error
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Roboto+Slab:400,700|Material+Icons">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.datatables.net/2.0.8/css/dataTables.bootstrap5.css">

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">

<link rel="stylesheet" href="/css/site.css?v=NEMRv3WSqXg5AzbEZ0pAP3Mcc_PFO0NPMmXvEtbRres">
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
    <symbol id="check2" viewBox="0 0 16 16">
        <path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z">
        </path>
    </symbol>
    <symbol id="circle-half" viewBox="0 0 16 16">
        <path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"></path>
    </symbol>
    <symbol id="moon-stars-fill" viewBox="0 0 16 16">
        <path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z">
        </path>
        <path d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z">
        </path>
    </symbol>
    <symbol id="sun-fill" viewBox="0 0 16 16">
        <path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z">
        </path>
    </symbol>
</svg>

<div class="dropdown position-fixed bottom-0 end-0 mb-3 me-3 bd-mode-toggle">

    <button class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center" id="bd-theme" type="button"
            aria-expanded="false" data-bs-toggle="dropdown" aria-label="Toggle theme (light)">
        <svg class="bi my-1 theme-icon-active" width="1em" height="1em">
            <use href="#sun-fill"></use>
        </svg>
        <span class="visually-hidden" id="bd-theme-text">Toggle theme</span>
    </button>

    <ul class="dropdown-menu dropdown-menu-end shadow" aria-labelledby="bd-theme-text">
        <li>
            <button type="button" class="dropdown-item d-flex align-items-center active" data-bs-theme-value="light"
                    aria-pressed="true">
                <svg class="bi me-2 opacity-50" width="1em" height="1em">
                    <use href="#sun-fill"></use>
                </svg>
                Light
                <svg class="bi ms-auto d-none" width="1em" height="1em">
                    <use href="#check2"></use>
                </svg>
            </button>
        </li>
        <li>
            <button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="dark"
                    aria-pressed="false">
                <svg class="bi me-2 opacity-50" width="1em" height="1em">
                    <use href="#moon-stars-fill"></use>
                </svg>
                Dark
                <svg class="bi ms-auto d-none" width="1em" height="1em">
                    <use href="#check2"></use>
                </svg>
            </button>
        </li>
        <li>
            <button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="auto"
                    aria-pressed="false">
                <svg class="bi me-2 opacity-50" width="1em" height="1em">
                    <use href="#circle-half"></use>
                </svg>
                Auto
                <svg class="bi ms-auto d-none" width="1em" height="1em">
                    <use href="#check2"></use>
                </svg>
            </button>

can you please check

I don’t understand why no one is helping me. All of these issues have been happening for almost four days now.

sure there will be someone able to help, but there is some update Iam sure @Mabaega is working to sort it out

1 Like


its still not working

Zatca EGS has just been updated to version v25.06.25.0001..

Let’s give it a try, and please report immediately if anything is not working properly.

1 Like

thanks .done.
now its working

Thank you for reporting it.

Please assist in trying it out, I made many changes to support the Prepayment Adjustment.

If you have business data for simulation environment, I would like someone to help check if the current version is working well like before or not.

2 Likes

Hi
when trying to make Cridet Note for invoice or invoice it giving the following message
{
“requestUri”: “https://gw-fatoora.zatca.gov.sa/e-invoicing/developer-portal/compliance/invoices”,
“requestType”: “Invoice Compliant Check”,
“statusCode”: “202-Accepted”,
“clearanceStatus”: “CLEARED”,
“validationResults”: {
“status”: “WARNING”,
“infoMessages”: [
{
“status”: “PASS”,
“type”: “INFO”,
“code”: “XSD_ZATCA_VALID”,
“category”: “XSD validation”,
“Message”: “Complied with UBL 2.1 standards in line with ZATCA specifications”
}
],
“warningMessages”: [
{
“status”: “WARNING”,
“type”: “WARNING”,
“code”: “BR-CO-16”,
“category”: “EN_16931”,
“Message”: “Amount due for payment (BT-115) = Invoice total amount with VAT (BT-112) -Pre-Paid amount (BT-113) + Rounding amount (BT-114).”
}
],
“errorMessages”:
}
}

Please share the generated XML and the Edit Form of the invoice you mentioned through PM.

Hi,
Please see the screenshot below for the issue being encountered in linking the tax invoice with zatca.


@Arslan786
Thank you for your report.
Regarding the error alert you shared, it indicates that the e-Invoice was successfully reported to ZATCA, but the system failed to write the server’s response back to the business data due to an API authorization issue. This issue appears to be intermittent and has only started occurring in the past few days.

At this time, the root cause is still unknown, and the only workaround available is a manual process. I will provide guidance so that you can capture the server’s response, as this will be required for future invoice reporting.
Please feel free to contact me via Private Message if you encounter any difficulties with this process.


As for the warning, please send the XML file along with a screenshot of the Invoice Edit Form, so I can further investigate the cause. It’s likely related to the latest ZATCA EGS update that was released today.

For @Arslan786 , and everyone who experiences something similar

Update Zatca Reference Error
image

ZATCA Invoice Fix Guide - Manual Form Modification

Make sure to back up your Business Data before trying it.

Overview

This guide helps you manually modify invoice forms when automatic processing fails, by temporarily disabling form validation scripts and updating ZATCA-related fields.

Step 1: Disable Form Validation Scripts

Modify CustomField Descriptions

You need to modify the following CustomFields:

  • Approval Status
  • Base64 QRCode
  • Zatca UUID
  • Zatca Certificate Info

For each CustomField:

  1. Go to SettingsCustom Fields
  2. Find and Edit the CustomField
  3. Locate the Description field
  4. Find the script tag that starts with:
    <script> document.addEventListener('DOMContentLoaded', () =>
    
  5. Add “xxx” at the beginning to disable it:
    <script>xxx document.addEventListener('DOMContentLoaded', () =>
    
  6. Save the changes

Purpose: This prevents the form locking script from running, allowing you to modify and save the form manually.

Step 2: Extract Values from Success Report

  1. Download the ZIP file generated after successful reporting/clearance
  2. Open the TXT file inside the ZIP archive
  3. Copy the following values from the TXT file:
    • ApprovalStatus
    • ZatcaUUID
    • Base64QrCode
    • ICV value (for Last ICV)
    • InvoiceHash value (for Last PIH)

Step 3: Update Business Details

  1. Go to SettingsBusiness Details
  2. Edit the business details
  3. Update the following fields:
    • Last ICV: Enter the ICV: value from the TXT file
    • Last PIH: Enter the InvoiceHash: value from the TXT file
  4. Save the changes

Step 4: Update Failed Invoice

  1. Open/Edit the invoice that failed processing
  2. Manually fill the following fields with values from the TXT file:
    • Approval Status: Enter the ApprovalStatus value
    • Zatca UUID: Enter the ZatcaUUID value
    • Base64 QRCode: Enter the Base64QrCode value
  3. Save the invoice

Step 5: Re-enable Form Validation Scripts

Restore CustomField Descriptions

  1. Go back to SettingsCustom Fields
  2. Edit each of the four CustomFields again:
    • Approval Status
    • Base64 QRCode
    • Zatca UUID
    • Zatca Certificate Info
  3. Remove the “xxx” from the script tags:
    <script>xxx document.addEventListener('DOMContentLoaded', () =>
    
    Should become:
    <script> document.addEventListener('DOMContentLoaded', () =>
    
  4. Save all changes

Important Notes

  • This process temporarily disables form validation to allow manual data entry
  • Always re-enable the scripts after completing the manual updates
  • Keep the TXT file from successful reports as reference for future manual fixes
  • This method should only be used when automatic ZATCA processing fails

Troubleshooting

  • If you can’t save the form, ensure all validation scripts are properly disabled (step 1)
  • Double-check that values copied from TXT file are complete and accurate
  • Verify that Last ICV and Last PIH are updated in Business Details before processing new invoices

:pushpin: Temporary Solution I Have Tried for Displaying QR Code Phase II

This is a temporary solution that I have personally tested and confirmed to work:

Make sure to back up your Business Data before trying it.

  1. Set a Custom Field (Base64 QR Code) to Appear on Printed Document
    Configure a custom field to contain the Base64 QR code, and ensure it is visible on the printed version of the invoice.

  1. Create a Custom Theme to Display QR Code Phase II
    Develop a custom invoice theme specifically designed to show the QR code from the custom field.
    Make sure to record the GUID of the custom theme you’ve created.

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	<!-- Styling for consistent look and feel -->
	<style>
		*, ::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;
		}

		table {
			font-size: 12px;
		}

		tr#table-headers th {
			font-weight: bold;
			padding: 5px 10px;
			border: 1px solid #000;
			text-align: start
		}

		tbody#table-rows td {
			padding: 5px 10px;
			border-left: 1px solid #000;
			border-right: 1px solid #000;
			text-align: start;
			vertical-align: top
		}

		tbody#table-rows tr:last-child td {
			padding-bottom: 30px;
			border-bottom: 1px solid #000;
		}
	</style>
</head>
<body>

	<!-- Top section: document title and optional business logo -->
	<table style="margin-bottom: 20px; width: 100%">
		<tbody>
			<tr>
				<td style="vertical-align: top">
					<div style="font-size: 32px; line-height: 32px; font-weight: bold" id="title">Invoice</div>
				</td>
				<td style="text-align: end" id="business-logo">
				</td>
			</tr>
		</tbody>
	</table>

	<!-- Second section: recipient info, document fields (like date, invoice #), and business info -->
	<table style="margin-bottom: 20px; width: 100%">
		<tbody>
			<tr>
				<td style="vertical-align: top" id="recipient-info"></td>
				<td style="text-align: end; vertical-align: top" id="fields"></td>
				<td style="width: 20px"></td>
				<td style="width: 1px; border-left-width: 1px; border-left-color: #000; border-left-style: solid"></td>
				<td style="width: 20px"></td>
				<td style="width: 1px; white-space: nowrap; vertical-align: top" id="business-info"></td>
			</tr>
		</tbody>
	</table>

	<!-- Description block -->
	<div style="font-weight: bold; font-size: 14px; margin-bottom: 20px" id="description"></div>

	<!-- Main table containing column headers, line items, and totals -->
	<table style="border-collapse: collapse; width: 100%">
		<thead>
			<tr id="table-headers"></tr>
		</thead>
		<tbody id="table-rows">
		</tbody>
		<tbody id="totals">
		</tbody>
	</table>

	<script src="resources/qrcode/qrcode.js"></script>
	<div id="qrcode" style="margin-bottom: 20px"></div>

	<!-- Section for any additional custom fields -->
	<div id="custom-fields"></div>

	<!-- Section for footers -->
	<table><tr><td><div id="footers"></div></td></tr></table>

	<!-- Section for final status (e.g. PAID, VOID) with special styling -->
	<div id="status" style="text-align: center"></div>

	<script>
		// Listen for messages sent from the parent frame (via postMessage API)
		window.addEventListener("message", (event) => {

			if (event.source !== window.parent) return; // Only accept messages from parent
			if (event.data.type !== 'context-response') return; // Ignore irrelevant messages

			// Extract the main data object sent from parent
			const data = event.data.body;

			// Set text direction (LTR or RTL) for the whole document
			document.documentElement.dir = data.direction;

			// Populate title and description if provided
			document.getElementById("title").innerHTML = data.title || "No title";
			document.getElementById("description").innerHTML = data.description || "";

			// Inject business logo image if available
			var businessLogoTd = document.getElementById("business-logo");
			if (data.business.logo) {
				const img = document.createElement("img");
				img.src = data.business.logo;
				img.style = "max-height: 150px; max-width: 300px; display: inline";
				businessLogoTd.appendChild(img);
			}

			// Populate business info section with name and address (convert line breaks)
			const business = data.business || {};
			document.getElementById("business-info").innerHTML = `<strong>${business.name || ""}</strong><br>${business.address ? business.address.replace(/\n/g, "<br>") : ""}`;

			// Populate recipient info section with name and address (convert line breaks)
			const recipient = data.recipient || {};
			document.getElementById("recipient-info").innerHTML = `<strong>${recipient.name || ""}</strong><br>${recipient.address ? recipient.address.replace(/\n/g, "<br>") : ""}`;

			// Insert fields (e.g. issue date, due date) into right-hand side
			const fieldsDiv = document.getElementById("fields");
			fieldsDiv.innerHTML = "";
			(data.fields || []).forEach(f => {
				const div = document.createElement("div");
				div.innerHTML = `<strong>${f.label}</strong><br />${f.text || ""}<br /><br />`;
				fieldsDiv.appendChild(div);
			});

			// Build table headers dynamically based on `columns` definition
			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);
			});

			// Populate main table body with rows and alignments based on column definitions
			const rowsBody = document.getElementById("table-rows");
			rowsBody.innerHTML = "";
			(data.table.rows || []).forEach(row => {
				const tr = document.createElement("tr");
				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);
			});

			// Populate totals section with subtotals, taxes, grand total, etc.
			const totalsBody = document.getElementById("totals");
			totalsBody.innerHTML = "";
			(data.table.totals || []).forEach(total => {
				const tr = document.createElement("tr");
				const tdLabel = document.createElement("td");
				const tdValue = document.createElement("td");
				tdLabel.innerHTML = total.label;
				tdLabel.colSpan = data.table.columns.length - 1;
				tdLabel.style = 'padding: 5px 10px; text-align: end; vertical-align: top';
				tdValue.innerHTML = total.text;
				tdValue.id = total.key;
				if (total.class) tdValue.classList.add(total.class);
				tdValue.dataset.value = total.number;
				tdValue.style = 'padding: 5px 10px; border: 1px solid #000; text-align: right; white-space: nowrap; vertical-align: top';

				// Bold totals if marked as 'emphasis'
				if (total.emphasis) {
					tdLabel.style.fontWeight = 'bold';
					tdValue.style.fontWeight = 'bold';
				}
				tr.appendChild(tdLabel);
				tr.appendChild(tdValue);
				totalsBody.appendChild(tr);
			});

			// Render custom fields section below the table (e.g. notes, terms)
			const customFieldsDiv = document.getElementById('custom-fields');
			customFieldsDiv.innerHTML = '';
			
			let qrcodetext = '';
			(data.custom_fields || []).forEach(f => {
                if (f.label === 'Base64 QRCode') {
                    qrcodetext = f.text;
                } else {
                    const div = document.createElement('div');
                    div.innerHTML = `<strong>${f.label}</strong><br />${(f.text || '').split('\n').join('<br />')}<br /><br />`;
                    if (f.displayAtTheTop === true) {
                        fieldsDiv.appendChild(div);
                    } else {
                        customFieldsDiv.appendChild(div);
                    }
                }
            });
            

			// Render footers section
			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);

				// Find and execute any script tags
				const scripts = div.querySelectorAll("script");
				scripts.forEach(script => {
					const newScript = document.createElement("script");

					// Copy script attributes if needed
					for (const attr of script.attributes) {
						newScript.setAttribute(attr.name, attr.value);
					}

					// Inline script handling
					if (script.textContent) {
						newScript.textContent = script.textContent;
					}

					// Replace the old <script> with the new one so it executes
					script.parentNode.replaceChild(newScript, script);
				});
			});

			// Display status label (e.g. PAID, CANCELLED) with colored border based on status type
			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);
			}

			const qrcodeDiv = document.getElementById('qrcode');
			
            // This handles QR code Phase II on invoices for Saudi Arabia
            if (qrcodetext && qrcodetext.length > 10 && qrcodeDiv) {
            	qrcodeDiv.innerHTML = '';
            	renderQRCode(qrcodetext, 'qrcode'); 
			} else {
				// This handles QR code on invoices for Saudi Arabia - this is here just temporarily. Better way will be implemented.
    			if (data.type === 'salesinvoice') {
    				if (data.business.country === 'ar-SA' || data.business.country === 'en-SA') {
    
    					function appendTLV(tag, text, byteList) {
    						const encoded = new TextEncoder().encode(text);
    						byteList.push(tag);
    						byteList.push(encoded.length);
    						for (let b of encoded) byteList.push(b);
    					}
    
    					const byteList = [];
    
    					let businessName = 'No name';
    					if (data.business) businessName = data.business.name;
    
    					let vatNumber = '0000000000000';
    					let vatField = data.business.custom_fields.find(item => item.key === 'd96d97e8-c857-42c6-8360-443c06a13de9');
    					if (vatField) vatNumber = vatField.text;
    
    					let timestamp = new Date((data.timestamp - 621355968000000000) / 10000).toISOString();
    
    					let total = 0;
    					let totalElement = document.getElementById('Total');
    					if (totalElement != null) total = parseFloat(totalElement.getAttribute('data-value'));
    
    					let vat = 0;
    					let taxAmounts = document.getElementsByClassName('taxAmount');
    					for (let i = 0; i < taxAmounts.length; i++) {
    						vat += parseFloat(taxAmounts[i].getAttribute('data-value'));
    					}
    
    					appendTLV(1, businessName, byteList);
    					appendTLV(2, vatNumber, byteList);
    					appendTLV(3, timestamp, byteList);
    					appendTLV(4, total.toFixed(2), byteList);
    					appendTLV(5, vat.toFixed(2), byteList);
    
    					// Convert to Uint8Array
    					const tlvBytes = Uint8Array.from(byteList);
    
    					// Convert to Base64
    					const qrData = btoa(String.fromCharCode(...tlvBytes));
    
    					console.log(qrData);
    
    					new QRCode(document.getElementById('qrcode'), {
    						text: qrData,
    						width: 128,
    						height: 128,
    						colorDark: '#000000',
    						colorLight: '#ffffff',
    						correctLevel: QRCode.CorrectLevel.L
    					});
    				}
    			}
			}
        
            function renderQRCode(content, elementId) {
                new QRCode(document.getElementById(elementId), {
                    text: content,
                    width: 160,
                    height: 160,
                    colorDark: '#000000',
                    colorLight: '#fafafa',
                    correctLevel: QRCode.CorrectLevel.L
                });
        
                // Tambahan opsional: parsing isi QR untuk tooltip
                let details = parseQRCodeContent(content);
                if (details.size > 0) {
                    let title = Array.from(details)
                        .map(([tag, value]) => `Tag ${tag} : ${value.join(', ')}`)
                        .join('\n');
                    const qrCodeDiv = document.getElementById(elementId);
                    if (qrCodeDiv) qrCodeDiv.title = title.trim();
                }
            }
        
            function parseQRCodeContent(qrCodeBase64) {
                let details = new Map();
                try {
                    let data = atob(qrCodeBase64.replace(/\+/g, '+'));
                    let index = 0;
                    while (index < data.length) {
                        let tag = data.charCodeAt(index++);
                        let length = data.charCodeAt(index++);
                        let value = data.substr(index, length);
                        index += length;
                        let decodedValue = (tag === 8 || tag === 9)
                            ? [...value].map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join(' ').toUpperCase()
                            : new TextDecoder('utf-8').decode(new Uint8Array([...value].map(c => c.charCodeAt(0))));
                        details.set(tag, [decodedValue]);
                    }
                } catch (ex) {
                    console.error('Error decoding QR code: ' + ex.message);
                }
                return details;
            }

		}, false);

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

</body>
</html>
  1. Batch Update All Reported Invoices
    Perform a batch update for all invoices that have already been REPORTED or CLEARED:
  • Remove the old footer containing the QR Code Phase II.
  • Apply the new custom theme (using the saved GUID) to those invoices.

  1. Remove QR Code Phase II Footers
    Go to Settings → Footers and delete the QrCodePhaseII footer from both Sales Invoices and Credit Notes, since it is no longer used.

  1. Continue Using the Custom Theme for New Invoices
    Going forward, use the custom theme for all future invoices to ensure the QR code displays correctly.