Print Quickbooks Checks

We’re switching from Quickbooks to Manager.io but we need to continue to print checks for payments.

With Quickbooks we used pre-printed voucher checks.

The top section is the check itself.

The middle section is an information voucher with a perforation that folds behind and is sent with the check.

The bottom section has the same information voucher but is torn off at the perforation and is kept for our records.

The following theme html code works with the Quickbooks checks we’ve been using.

We’re posting it here in case it’s useful to others.

<!-- based on code from a post from laltomare on:
https://forum.manager.io/t/printing-cheques-from-manager/47445/6
-->

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    
    <style>
        @page {
            size: 8.5in 11in;
            margin: 0;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            width: 8.5in;
            height: 11in;
            font-family: 'Arial', 'Times New Roman', serif;
            font-size: 12px;
            position: relative;
            margin: 0;
            padding: 0;
            background: white;
        }

        /* Hide any inherited Manager.io template elements */
        header, #title, #business-logo, #recipient-info, #fields, #business-info, 
        #description, #table-headers, #table-rows, #custom-fields, #footers, #status, #qrcode {
            display: none !important;
            visibility: hidden !important;
        }

        .check-section {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            height: 3.5in;
        }

        .date-line {
            position: absolute;
            top:   0.77in;
            right: 0.55in;
            display: flex;
            align-items: center;
        }

        .date-value {
//            padding: 2px 8px;
            min-width: 120px;
            text-align: right;
            font-weight: bold;
        }

        .amount-line {
            position: absolute;
            top:   1.30in;
            left:  7.00in;
            right: 0.40in;
            text-align: left;
            font-weight: bold;
            font-size: 14px;
            flex-shrink: 0;
            display: flex;
        }

        .amount-dotted-line {
            flex: 1;
            border-top: 2px dotted #000;
            margin-top: 7px;
            margin-left: 5px;
            min-width: 20px;
        }

        .payee-line {
            position: absolute;
            top:   1.33in;
            left:  1.03in;
            right: 1.80in;
            display: flex;
            align-items: flex-end;
        }

        .payee-text {
            padding-right: 8px;
            white-space: nowrap;
        }

        .payee-dotted-line {
            flex: 1;
            border-top: 2px dotted #000;
            margin-bottom: 7px;
            min-width: 20px;
        }

        .amount-words-line {
            position: absolute;
            top:   1.68in;
            left:  0.50in;
            right: 0.90in;
            display: flex;
            align-items: flex-end;
        }

        .amount-words-text {
            padding-right: 8px;
            white-space: nowrap;
        }

        .amount-words-dotted-line {
            flex: 1;
            border-top: 2px dotted #000;
            margin-bottom: 7px;
            min-width: 20px;
        }

        .recipient-addr-line {
            position: absolute;
            top:   2.05in;
            left:  0.47in;
            right: 0.90in;
            display: flex;
            align-items: baseline;
            padding-bottom: 4px;
        }

        .recipient-addr-text {
            padding-right: 8px;
        }

        .memo-line {
            position: absolute;
            top:   2.75in;
            left:  0.74in;
            right: 3.60in;
            display: flex;
        }

        .memo-value {
            display: inline-block;
            min-width: 250px;
            padding: 2px 8px;
        }

        .perforation {
            position: absolute;
            left: 15px;
            right: 15px;
            height: 0;
//            border-top: 3px dashed #999;
        }

        .voucher-section {
            position: absolute;
            left: 0.25in;
            right: 0.25in;
//            background: #fafafa;
        }

        .voucher-1 {
            top: 3.5in;
            height: 3.50in;
//            border-top: 1px solid #000;
//            border-bottom: 1px solid #000;
        }

        .voucher-2 {
            bottom: 0.48in;
            height: 3.50in;
//            border-top: 1px solid #000;
        }

        .voucher-content {
            padding: 5px;
        }

        .voucher-grid {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 5px;
            margin-bottom: 5px;
        }

        .voucher-field {
            display: flex;
        }

        .voucher-field label {
            font-size: 9px;
            color: #666;
            text-transform: uppercase;
            padding: 2px 2px;
//            margin-bottom: 3px;
            font-weight: bold;
        }

        .voucher-field .value {
            border-bottom: 1px solid #ccc;
            padding: 2px 2px;
            font-family: 'Courier New', monospace;
            font-size: 11px;
//            min-height: 20px;
            background: white;
        }

        .voucher-table {
            width: 100%;
            border-collapse: collapse;
            font-size: 10px;
            margin-bottom: 10px;
        }

        .voucher-table th,
        .voucher-table td {
            border: 1px solid #000;
            padding: 2px 2px;
            text-align: left;
        }

        .voucher-table th {
            background: #e0e0e0;
            font-weight: bold;
            text-transform: uppercase;
            font-size: 9px;
        }

    </style>
</head>
<body>
    <!-- CHECK SECTION -->
    <div class="check-section">
        <!-- Date -->
        <div class="date-line">
            <span class="date-value" id="check-date"></span>
        </div>
        
        <div class="amount-line">
			<span id="amount-numeric"></span>
            <span class="amount-dotted-line"></span>
		</div>

        <!-- Payee Line -->
        <div class="payee-line">
            <span class="payee-text" id="payee-name"></span>
            <span class="payee-dotted-line"></span>
        </div>
        
        <!-- Amount in Words -->
        <div class="amount-words-line">
            <span class="amount-words-text" id="amount-words"></span>
            <span class="amount-words-dotted-line"></span>
        </div>
        
        <!-- Memo -->
        <div class="memo-line">
            <span class="memo-value" id="memo-text"></span>
        </div>

        <!-- Address -->
        <div class="recipient-addr-line">
            <span class="recipient-addr-text" id="recipient-addr"></span>
        </div>

    </div>
    
    <div class="perforation" style="top: 3.5in;"></div>
    
    <!-- VOUCHER 1 -->
    <div class="voucher-section voucher-1">
        <div class="voucher-content">
            <div class="voucher-grid">
                <div class="voucher-field">
                    <label>Date:</label>
                    <div class="value" id="voucher1-date"></div>
                </div>
                <div class="voucher-field">
                    <label>Check #:</label>
                    <div class="value" id="voucher1-number"></div>
                </div>
                <div class="voucher-field">
                    <label>Pay To:</label>
                    <div class="value" id="voucher1-payee"></div>
                </div>
                <div class="voucher-field">
                    <label>Amount:</label>
                    <div class="value" id="voucher1-amount"></div>
                </div>
                <div class="voucher-field" style="grid-column: 1 / -1;">
                    <label>Memo:</label>
                    <div class="value" id="voucher1-memo"></div>
                </div>
            </div>
            
            <table class="voucher-table">
                <thead>
					<tr id="voucher1-table-headers"></tr>
                </thead>
                <tbody id="voucher1-rows">
                    <!-- Rows will be inserted here -->
                </tbody>
            </table>
        </div>
    </div>
    
    <div class="perforation" style="bottom: 3.98in;"></div>
    
    <!-- VOUCHER 2 -->
    <div class="voucher-section voucher-2">
        <div class="voucher-content">
            <div class="voucher-grid">
                <div class="voucher-field">
                    <label>Date:</label>
                    <div class="value" id="voucher2-date"></div>
                </div>
                <div class="voucher-field">
                    <label>Check #:</label>
                    <div class="value" id="voucher2-number"></div>
                </div>
                <div class="voucher-field">
                    <label>Pay To:</label>
                    <div class="value" id="voucher2-payee"></div>
                </div>
                <div class="voucher-field">
                    <label>Amount"</label>
                    <div class="value" id="voucher2-amount"></div>
                </div>
                <div class="voucher-field" style="grid-column: 1 / -1;">
                    <label>Memo:</label>
                    <div class="value" id="voucher2-memo"></div>
                </div>
            </div>
            
            <table class="voucher-table">
                <thead>
					<tr id="voucher2-table-headers"></tr>
                </thead>
                <tbody id="voucher2-rows">
                    <!-- Rows will be inserted here -->
                </tbody>
            </table>
            
        </div>
    </div>

    <script>
        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;
            
            // Extract data using Manager.io's postMessage API
            const recipient = data.recipient || {};
            const fields = data.fields || [];
            const totals = data.table?.totals || [];
            const rows = data.table?.rows || [];
            
            // Find date field
            const dateField = fields.find(f => 
                f.label && f.label.toLowerCase().includes('date')
            );
            const dateValue = dateField?.text || '';
            
            // Find check number field
            const numberField = fields.find(f => 
                f.label && (f.label.toLowerCase().includes('number') || 
                           f.label.toLowerCase().includes('reference') ||
                           f.label.toLowerCase().includes('check'))
            );
            const numberValue = numberField?.text || '';
            
            // Find amount from totals
            const amountTotal = totals.find(t => t.key === 'Total') || totals[totals.length - 1];
            const amountValue = amountTotal?.text || '';
            
            // Populate CHECK section
            document.getElementById('check-date').textContent = dateValue;
            document.getElementById('payee-name').textContent = recipient.name || '';
            
            // Remove $ from amount value if present, since $ is already in HTML
//            let cleanAmount = amountValue.replace(/^\$/, '').replace(/^USD/, '').replace(/^\,/, '').trim();
            let commaAmount = amountValue.replace('$', '');
            let cleanAmount = commaAmount.replace(',', '');
            document.getElementById('amount-numeric').textContent = commaAmount;

			// recipient address
			document.getElementById("recipient-addr").innerHTML = `${recipient.address ? recipient.address.replace(/\n/g, "<br>") : ""}`;
//			document.getElementById("recipient-name").textContent = recipient.name    || '';
//			document.getElementById("recipient-addr").textContent = recipient.address || '';
            
            document.getElementById('memo-text').textContent = data.description || '';
            
            // Amount in words - convert numeric amount to words
            document.getElementById('amount-words').textContent = numberToWords(cleanAmount);
            
// VOUCHER 1
//            document.getElementById('voucher1-checknum').textContent = numberValue ? 'Check #' + numberValue : '';
            document.getElementById('voucher1-date').textContent = dateValue;
            document.getElementById('voucher1-number').textContent = numberValue;
            document.getElementById('voucher1-payee').textContent = recipient.name || '';
            document.getElementById('voucher1-amount').textContent = amountValue;
            document.getElementById('voucher1-memo').textContent = data.description || '';

			// TABLE HEADERS
			// Build column headers dynamically based on data.table.columns
			{
				const headersRow = document.getElementById("voucher1-table-headers");
				headersRow.innerHTML = "";
				(data.table.columns || []).forEach(col => {
					const th = document.createElement("th");
					th.innerHTML = col.label; // Column header text
					th.style.textAlign = col.align; // left, center, or right
					
					// Column width options:
					if (col.minWidth) {
						// Minimum width column (typically for numbers)
						th.style.whiteSpace = 'nowrap';
						th.style.width = '1px';
					}
					else if (col.nowrap) {
						// No-wrap column with fixed width
						th.style.whiteSpace = 'nowrap';
						th.style.width = '80px';
					}
					headersRow.appendChild(th);
				});

				// LINE ITEMS
				// Populate table with line items (products, services, etc.)
				const rowsBody = document.getElementById("voucher1-rows");
				rowsBody.innerHTML = "";
				(data.table.rows || []).forEach(row => {
					const tr = document.createElement("tr");
					tr.className = 'row'; // Apply row styling
					
					// Create cells for each column
					row.cells.forEach((cell, i) => {
						var col = data.table.columns[i]; // Get column definition
						const td = document.createElement("td");
						// Convert newlines to HTML line breaks
						td.innerHTML = (cell.text || "").split("\n").join("<br />");
						// Apply column alignment
						td.style.textAlign = col.align;
						
						// Apply column width settings
						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);
				});

				// MARK LAST ROW
				// Add special styling to the last line item row
				const vrows = rowsBody.querySelectorAll('tr.row');

				if (vrows.length > 0) {
					const lastRow = vrows[vrows.length - 1];
					lastRow.classList.add('last-row'); // Adds bottom border and extra padding
				}

				// TOTALS SECTION
				// Display subtotal, taxes, discounts, and grand total
				(data.table.totals || []).forEach(total => {
					const tr = document.createElement("tr");
	//				tr.className = 'total';
					
					// Label cell (e.g., "Subtotal:", "Tax:", "Total:")
					const tdLabel = document.createElement("td");
					tdLabel.innerHTML = total.label;
					tdLabel.style.textAlign = 'end';
					tdLabel.colSpan = data.table.columns.length - 1; // Span all columns except last
					
					// Value cell (the amount)
					const tdValue = document.createElement("td");
					tdValue.innerHTML = total.text; // Formatted amount
					tdValue.style.textAlign = 'end';
					tdValue.dataset.value = total.number; // Raw numeric value for calculations

					// Bold formatting for important totals
					if (total.emphasis) {
						tdLabel.style.fontWeight = 'bold';
						tdValue.style.fontWeight = 'bold';
					}
					tr.appendChild(tdLabel);
					tr.appendChild(tdValue);
					rowsBody.appendChild(tr);
				});
			}

// VOUCHER 2
//            document.getElementById('voucher2-checknum').textContent = numberValue ? 'Check #' + numberValue : '';
            document.getElementById('voucher2-date').textContent = dateValue;
            document.getElementById('voucher2-number').textContent = numberValue;
            document.getElementById('voucher2-payee').textContent = recipient.name || '';
            document.getElementById('voucher2-amount').textContent = amountValue;
            document.getElementById('voucher2-memo').textContent = data.description || '';
            
			// TABLE HEADERS
			// Build column headers dynamically based on data.table.columns
			{
				const headersRow = document.getElementById("voucher2-table-headers");
				headersRow.innerHTML = "";
				(data.table.columns || []).forEach(col => {
					const th = document.createElement("th");
					th.innerHTML = col.label; // Column header text
					th.style.textAlign = col.align; // left, center, or right
					
					// Column width options:
					if (col.minWidth) {
						// Minimum width column (typically for numbers)
						th.style.whiteSpace = 'nowrap';
						th.style.width = '1px';
					}
					else if (col.nowrap) {
						// No-wrap column with fixed width
						th.style.whiteSpace = 'nowrap';
						th.style.width = '80px';
					}
					headersRow.appendChild(th);
				});

				// LINE ITEMS
				// Populate table with line items (products, services, etc.)
				const rowsBody = document.getElementById("voucher2-rows");
				rowsBody.innerHTML = "";
				(data.table.rows || []).forEach(row => {
					const tr = document.createElement("tr");
					tr.className = 'row'; // Apply row styling
					
					// Create cells for each column
					row.cells.forEach((cell, i) => {
						var col = data.table.columns[i]; // Get column definition
						const td = document.createElement("td");
						// Convert newlines to HTML line breaks
						td.innerHTML = (cell.text || "").split("\n").join("<br />");
						// Apply column alignment
						td.style.textAlign = col.align;
						
						// Apply column width settings
						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);
				});

				// MARK LAST ROW
				// Add special styling to the last line item row
				const vrows = rowsBody.querySelectorAll('tr.row');

				if (vrows.length > 0) {
					const lastRow = vrows[vrows.length - 1];
					lastRow.classList.add('last-row'); // Adds bottom border and extra padding
				}

				// TOTALS SECTION
				// Display subtotal, taxes, discounts, and grand total
				(data.table.totals || []).forEach(total => {
					const tr = document.createElement("tr");
	//				tr.className = 'total';
					
					// Label cell (e.g., "Subtotal:", "Tax:", "Total:")
					const tdLabel = document.createElement("td");
					tdLabel.innerHTML = total.label;
					tdLabel.style.textAlign = 'end';
					tdLabel.colSpan = data.table.columns.length - 1; // Span all columns except last
					
					// Value cell (the amount)
					const tdValue = document.createElement("td");
					tdValue.innerHTML = total.text; // Formatted amount
					tdValue.style.textAlign = 'end';
					tdValue.dataset.value = total.number; // Raw numeric value for calculations

					// Bold formatting for important totals
					if (total.emphasis) {
						tdLabel.style.fontWeight = 'bold';
						tdValue.style.fontWeight = 'bold';
					}
					tr.appendChild(tdLabel);
					tr.appendChild(tdValue);
					rowsBody.appendChild(tr);
				});
			}

            sendResize();
        }, false);

        window.addEventListener("load", () => {
            window.parent.postMessage({ type: "context-request" }, "*");
        });

        // Number to words converter function
        function numberToWords(amount) {
            // Parse amount string like "76.46"
//            const match = amount.match(/^(\d+\.?\d*)$/);
//            if (!match) return '!match' + match + amount; // Return as-is if not a number
            
            const parts   = amount.split('.');
            const dollars = parseInt(parts[0]) || 0;
            const cents   = parts[1] ? parseInt(parts[1]) : 0;
            const hundreds  = dollars%1000;
            const thousands = dollars/1000;

//			return 'parts[0]=' + parts[0] + 'parts[1]=' + parts[1] + 'parts[2]=' + parts[2] + ' dollars=' + dollars + 'cents=' + cents;
            
            // Convert dollars to words
            const ones = ['', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine',
                          'Ten', 'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen',
                          'Seventeen', 'Eighteen', 'Nineteen'];
            const tens = ['', '', 'Twenty', 'Thirty', 'Forty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninety'];
            
            function convertHundreds(num) 
			{
                if (num === 0) 
					return '';

                let words = '';
                
                if (num >= 100) 
				{
//                    words += 'A' + num +' ';
                    words += ones[Math.floor(num / 100)] + ' Hundred ';
                    num %= 100;
                }
                
                if (num >= 20) 
				{
//                    words += 'B' + num +' ';
                    words += tens[Math.floor(num / 10)] + ' ';
                    num %= 10;
                }
                
                if (num > 0) 
				{
//                    words += 'C' + num +' ';
                    words += ones[num] + ' ';
                }
                
                return words.trim();
            }
            
            let result = '';
			if (dollars >=1000)
			{
				result += convertHundreds(Math.trunc(dollars/1000)) + ' Thousand '
			}
			
			result += convertHundreds(dollars%1000);
			
            // Add cents
            result += ' and ' + cents.toString().padStart(2, '0') + '/100';
            
            return result;
        }
    </script>
</body>
</html>
4 Likes

Very nice! Thank you for sharing.

This version doesn’t show the account name on the voucher that goes with the check.

<!-- based on code from a post from laltomare on:
https://forum.manager.io/t/printing-cheques-from-manager/47445/6
-->

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    
    <style>
        @page {
            size: 8.5in 11in;
            margin: 0;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            width: 8.5in;
            height: 11in;
            font-family: 'Arial', 'Times New Roman', serif;
            font-size: 12px;
            position: relative;
            margin: 0;
            padding: 0;
            background: white;
        }

        /* Hide any inherited Manager.io template elements */
        header, #title, #business-logo, #recipient-info, #fields, #business-info, 
        #description, #table-headers, #table-rows, #custom-fields, #footers, #status, #qrcode {
            display: none !important;
            visibility: hidden !important;
        }

        .check-section {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            height: 3.5in;
        }

        .date-line {
            position: absolute;
            top:   0.77in;
            right: 0.55in;
            display: flex;
            align-items: center;
        }

        .date-value {
//            padding: 2px 8px;
            min-width: 120px;
            text-align: right;
            font-weight: bold;
        }

        .amount-line {
            position: absolute;
            top:   1.30in;
            left:  7.00in;
            right: 0.40in;
            text-align: left;
            font-weight: bold;
            font-size: 14px;
            flex-shrink: 0;
            display: flex;
        }

        .amount-dotted-line {
            flex: 1;
            border-top: 2px dotted #000;
            margin-top: 7px;
            margin-left: 5px;
            min-width: 20px;
        }

        .payee-line {
            position: absolute;
            top:   1.33in;
            left:  1.03in;
            right: 1.80in;
            display: flex;
            align-items: flex-end;
        }

        .payee-text {
            padding-right: 8px;
            white-space: nowrap;
        }

        .payee-dotted-line {
            flex: 1;
            border-top: 2px dotted #000;
            margin-bottom: 7px;
            min-width: 20px;
        }

        .amount-words-line {
            position: absolute;
            top:   1.68in;
            left:  0.50in;
            right: 0.90in;
            display: flex;
            align-items: flex-end;
        }

        .amount-words-text {
            padding-right: 8px;
            white-space: nowrap;
        }

        .amount-words-dotted-line {
            flex: 1;
            border-top: 2px dotted #000;
            margin-bottom: 7px;
            min-width: 20px;
        }

        .recipient-addr-line {
            position: absolute;
            top:   2.05in;
            left:  0.47in;
            right: 0.90in;
            display: flex;
            align-items: baseline;
            padding-bottom: 4px;
        }

        .recipient-addr-text {
            padding-right: 8px;
        }

        .memo-line {
            position: absolute;
            top:   2.75in;
            left:  0.74in;
            right: 3.60in;
            display: flex;
        }

        .memo-value {
            display: inline-block;
            min-width: 250px;
            padding: 2px 8px;
        }

        .perforation {
            position: absolute;
            left: 15px;
            right: 15px;
            height: 0;
//            border-top: 3px dashed #999;
        }

        .voucher-section {
            position: absolute;
            left: 0.25in;
            right: 0.25in;
//            background: #fafafa;
        }

        .voucher-1 {
            top: 3.5in;
            height: 3.50in;
//            border-top: 1px solid #000;
//            border-bottom: 1px solid #000;
        }

        .voucher-2 {
            bottom: 0.48in;
            height: 3.50in;
//            border-top: 1px solid #000;
        }

        .voucher-content {
            padding: 5px;
        }

        .voucher-grid {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 5px;
            margin-bottom: 5px;
        }

        .voucher-field {
            display: flex;
        }

        .voucher-field label {
            font-size: 9px;
            color: #666;
            text-transform: uppercase;
            padding: 2px 2px;
//            margin-bottom: 3px;
            font-weight: bold;
        }

        .voucher-field .value {
            border-bottom: 1px solid #ccc;
            padding: 2px 2px;
            font-family: 'Courier New', monospace;
            font-size: 11px;
//            min-height: 20px;
            background: white;
        }

        .voucher-table {
            width: 100%;
            border-collapse: collapse;
            font-size: 10px;
            margin-bottom: 10px;
        }

        .voucher-table th,
        .voucher-table td {
            border: 1px solid #000;
            padding: 2px 2px;
            text-align: left;
        }

        .voucher-table th {
            background: #e0e0e0;
            font-weight: bold;
            text-transform: uppercase;
            font-size: 9px;
        }

    </style>
</head>
<body>
<!--	<p id="debug" style="overflow-wrap: break-word"></p> -->

    <!-- CHECK SECTION -->
    <div class="check-section">
        <!-- Date -->
        <div class="date-line">
            <span class="date-value" id="check-date"></span>
        </div>
        
        <div class="amount-line">
			<span id="amount-numeric"></span>
            <span class="amount-dotted-line"></span>
		</div>

        <!-- Payee Line -->
        <div class="payee-line">
            <span class="payee-text" id="payee-name"></span>
            <span class="payee-dotted-line"></span>
        </div>
        
        <!-- Amount in Words -->
        <div class="amount-words-line">
            <span class="amount-words-text" id="amount-words"></span>
            <span class="amount-words-dotted-line"></span>
        </div>
        
        <!-- Memo -->
        <div class="memo-line">
            <span class="memo-value" id="memo-text"></span>
        </div>

        <!-- Address -->
        <div class="recipient-addr-line">
            <span class="recipient-addr-text" id="recipient-addr"></span>
        </div>

    </div>
    
    <div class="perforation" style="top: 3.5in;"></div>
    
    <!-- VOUCHER 1 -->
    <div class="voucher-section voucher-1">
        <div class="voucher-content">
            <div class="voucher-grid">
                <div class="voucher-field">
                    <label>Date:</label>
                    <div class="value" id="voucher1-date"></div>
                </div>
                <div class="voucher-field">
                    <label>Check #:</label>
                    <div class="value" id="voucher1-number"></div>
                </div>
                <div class="voucher-field">
                    <label>Pay To:</label>
                    <div class="value" id="voucher1-payee"></div>
                </div>
                <div class="voucher-field">
                    <label>Amount:</label>
                    <div class="value" id="voucher1-amount"></div>
                </div>
                <div class="voucher-field" style="grid-column: 1 / -1;">
                    <label>Memo:</label>
                    <div class="value" id="voucher1-memo"></div>
                </div>
            </div>
            
            <table class="voucher-table">
                <thead>
					<tr id="voucher1-table-headers"></tr>
                </thead>
                <tbody id="voucher1-rows">
                    <!-- Rows will be inserted here -->
                </tbody>
            </table>
        </div>
    </div>
    
    <div class="perforation" style="bottom: 3.98in;"></div>
    
    <!-- VOUCHER 2 -->
    <div class="voucher-section voucher-2">
        <div class="voucher-content">
            <div class="voucher-grid">
                <div class="voucher-field">
                    <label>Date:</label>
                    <div class="value" id="voucher2-date"></div>
                </div>
                <div class="voucher-field">
                    <label>Check #:</label>
                    <div class="value" id="voucher2-number"></div>
                </div>
                <div class="voucher-field">
                    <label>Pay To:</label>
                    <div class="value" id="voucher2-payee"></div>
                </div>
                <div class="voucher-field">
                    <label>Amount"</label>
                    <div class="value" id="voucher2-amount"></div>
                </div>
                <div class="voucher-field" style="grid-column: 1 / -1;">
                    <label>Memo:</label>
                    <div class="value" id="voucher2-memo"></div>
                </div>
            </div>
            
            <table class="voucher-table">
                <thead>
					<tr id="voucher2-table-headers"></tr>
                </thead>
                <tbody id="voucher2-rows">
                    <!-- Rows will be inserted here -->
                </tbody>
            </table>
            
        </div>
    </div>

    <script>
        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;
            
            // Extract data using Manager.io's postMessage API
            const recipient = data.recipient || {};
            const fields = data.fields || [];
            const totals = data.table?.totals || [];
            const rows = data.table?.rows || [];
            
            // Find date field
            const dateField = fields.find(f => 
                f.label && f.label.toLowerCase().includes('date')
            );
            const dateValue = dateField?.text || '';
            
            // Find check number field
            const numberField = fields.find(f => 
                f.label && (f.label.toLowerCase().includes('number') || 
                           f.label.toLowerCase().includes('reference') ||
                           f.label.toLowerCase().includes('check'))
            );
            const numberValue = numberField?.text || '';
            
            // Find amount from totals
            const amountTotal = totals.find(t => t.key === 'Total') || totals[totals.length - 1];
            const amountValue = amountTotal?.text || '';
            
            // Populate CHECK section
            document.getElementById('check-date').textContent = dateValue;
            document.getElementById('payee-name').textContent = recipient.name || '';
            
            // Remove $ from amount value if present, since $ is already in HTML
//            let cleanAmount = amountValue.replace(/^\$/, '').replace(/^USD/, '').replace(/^\,/, '').trim();
            let commaAmount = amountValue.replace('$', '');
            let cleanAmount = commaAmount.replace(',', '');
            document.getElementById('amount-numeric').textContent = commaAmount;

			// recipient address
			document.getElementById("recipient-addr").innerHTML = `${recipient.address ? recipient.address.replace(/\n/g, "<br>") : ""}`;
//			document.getElementById("recipient-name").textContent = recipient.name    || '';
//			document.getElementById("recipient-addr").textContent = recipient.address || '';
            
            document.getElementById('memo-text').textContent = data.description || '';
            
            // Amount in words - convert numeric amount to words
            document.getElementById('amount-words').textContent = numberToWords(cleanAmount);
            
// VOUCHER 1
//            document.getElementById('voucher1-checknum').textContent = numberValue ? 'Check #' + numberValue : '';
            document.getElementById('voucher1-date').textContent = dateValue;
            document.getElementById('voucher1-number').textContent = numberValue;
            document.getElementById('voucher1-payee').textContent = recipient.name || '';
            document.getElementById('voucher1-amount').textContent = amountValue;
            document.getElementById('voucher1-memo').textContent = data.description || '';

			// TABLE HEADERS
			// Build column headers dynamically based on data.table.columns
			{
/*				const DebugDiv = document.getElementById("debug");
				var debugText  = Object.keys(data.table.columns) + '<br>';
				debugText += Object.keys(data.table.rows) + '<br>';
//				debugText += Object.keys(data.table.rows.cells) + '<br>';
				DebugDiv.innerHTML  = debugText + '<br>';
*/
				const headersRow = document.getElementById("voucher1-table-headers");
				headersRow.innerHTML = "";
				(data.table.columns || []).forEach(col => 
				{
					const th = document.createElement("th");
					th.innerHTML = col.label; // Column header text
					th.style.textAlign = col.align; // left, center, or right
					
					// Column width options:
					if (col.minWidth) 
					{
						// Minimum width column (typically for numbers)
						th.style.whiteSpace = 'nowrap';
						th.style.width = '1px';
					}
					else 
					if (col.nowrap) 
					{
						// No-wrap column with fixed width
						th.style.whiteSpace = 'nowrap';
						th.style.width = '80px';
					}
					headersRow.appendChild(th);
				});

				// LINE ITEMS
				// Populate table with line items (products, services, etc.)
				const rowsBody = document.getElementById("voucher1-rows");
				rowsBody.innerHTML = "";
				(data.table.rows || []).forEach(row => 
				{
					const tr = document.createElement("tr");
					tr.className = 'row'; // Apply row styling
					
					// Create cells for each column
					row.cells.forEach((cell, i) => 
					{
						var col = data.table.columns[i]; // Get column definition
						const td = document.createElement("td");
						if (col.label!='Account')
						{
							// Convert newlines to HTML line breaks
							td.innerHTML = (cell.text || "").split("\n").join("<br />");
						}
						else
						{
							td.innerHTML = '***';
						}
						// Apply column alignment
						td.style.textAlign = col.align;
						
						// Apply column width settings
						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);
				});

				// MARK LAST ROW
				// Add special styling to the last line item row
				const vrows = rowsBody.querySelectorAll('tr.row');

				if (vrows.length > 0) {
					const lastRow = vrows[vrows.length - 1];
					lastRow.classList.add('last-row'); // Adds bottom border and extra padding
				}

				// TOTALS SECTION
				// Display subtotal, taxes, discounts, and grand total
				(data.table.totals || []).forEach(total => {
					const tr = document.createElement("tr");
	//				tr.className = 'total';
					
					// Label cell (e.g., "Subtotal:", "Tax:", "Total:")
					const tdLabel = document.createElement("td");
					tdLabel.innerHTML = total.label;
					tdLabel.style.textAlign = 'end';
					tdLabel.colSpan = data.table.columns.length - 1; // Span all columns except last
					
					// Value cell (the amount)
					const tdValue = document.createElement("td");
					tdValue.innerHTML = total.text; // Formatted amount
					tdValue.style.textAlign = 'end';
					tdValue.dataset.value = total.number; // Raw numeric value for calculations

					// Bold formatting for important totals
					if (total.emphasis) {
						tdLabel.style.fontWeight = 'bold';
						tdValue.style.fontWeight = 'bold';
					}
					tr.appendChild(tdLabel);
					tr.appendChild(tdValue);
					rowsBody.appendChild(tr);
				});
			}

// VOUCHER 2
//            document.getElementById('voucher2-checknum').textContent = numberValue ? 'Check #' + numberValue : '';
            document.getElementById('voucher2-date').textContent = dateValue;
            document.getElementById('voucher2-number').textContent = numberValue;
            document.getElementById('voucher2-payee').textContent = recipient.name || '';
            document.getElementById('voucher2-amount').textContent = amountValue;
            document.getElementById('voucher2-memo').textContent = data.description || '';
            
			// TABLE HEADERS
			// Build column headers dynamically based on data.table.columns
			{
				const headersRow = document.getElementById("voucher2-table-headers");
				headersRow.innerHTML = "";
				(data.table.columns || []).forEach(col => {
					const th = document.createElement("th");
					th.innerHTML = col.label; // Column header text
					th.style.textAlign = col.align; // left, center, or right
					
					// Column width options:
					if (col.minWidth) {
						// Minimum width column (typically for numbers)
						th.style.whiteSpace = 'nowrap';
						th.style.width = '1px';
					}
					else if (col.nowrap) {
						// No-wrap column with fixed width
						th.style.whiteSpace = 'nowrap';
						th.style.width = '80px';
					}
					headersRow.appendChild(th);
				});

				// LINE ITEMS
				// Populate table with line items (products, services, etc.)
				const rowsBody = document.getElementById("voucher2-rows");
				rowsBody.innerHTML = "";
				(data.table.rows || []).forEach(row => {
					const tr = document.createElement("tr");
					tr.className = 'row'; // Apply row styling
					
					// Create cells for each column
					row.cells.forEach((cell, i) => {
						var col = data.table.columns[i]; // Get column definition
						const td = document.createElement("td");
						// Convert newlines to HTML line breaks
						td.innerHTML = (cell.text || "").split("\n").join("<br />");
						// Apply column alignment
						td.style.textAlign = col.align;
						
						// Apply column width settings
						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);
				});

				// MARK LAST ROW
				// Add special styling to the last line item row
				const vrows = rowsBody.querySelectorAll('tr.row');

				if (vrows.length > 0) {
					const lastRow = vrows[vrows.length - 1];
					lastRow.classList.add('last-row'); // Adds bottom border and extra padding
				}

				// TOTALS SECTION
				// Display subtotal, taxes, discounts, and grand total
				(data.table.totals || []).forEach(total => {
					const tr = document.createElement("tr");
	//				tr.className = 'total';
					
					// Label cell (e.g., "Subtotal:", "Tax:", "Total:")
					const tdLabel = document.createElement("td");
					tdLabel.innerHTML = total.label;
					tdLabel.style.textAlign = 'end';
					tdLabel.colSpan = data.table.columns.length - 1; // Span all columns except last
					
					// Value cell (the amount)
					const tdValue = document.createElement("td");
					tdValue.innerHTML = total.text; // Formatted amount
					tdValue.style.textAlign = 'end';
					tdValue.dataset.value = total.number; // Raw numeric value for calculations

					// Bold formatting for important totals
					if (total.emphasis) {
						tdLabel.style.fontWeight = 'bold';
						tdValue.style.fontWeight = 'bold';
					}
					tr.appendChild(tdLabel);
					tr.appendChild(tdValue);
					rowsBody.appendChild(tr);
				});
			}

            sendResize();
        }, false);

        window.addEventListener("load", () => {
            window.parent.postMessage({ type: "context-request" }, "*");
        });

        // Number to words converter function
        function numberToWords(amount) {
            // Parse amount string like "76.46"
//            const match = amount.match(/^(\d+\.?\d*)$/);
//            if (!match) return '!match' + match + amount; // Return as-is if not a number
            
            const parts   = amount.split('.');
            const dollars = parseInt(parts[0]) || 0;
            const cents   = parts[1] ? parseInt(parts[1]) : 0;
            const hundreds  = dollars%1000;
            const thousands = dollars/1000;

//			return 'parts[0]=' + parts[0] + 'parts[1]=' + parts[1] + 'parts[2]=' + parts[2] + ' dollars=' + dollars + 'cents=' + cents;
            
            // Convert dollars to words
            const ones = ['', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine',
                          'Ten', 'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen',
                          'Seventeen', 'Eighteen', 'Nineteen'];
            const tens = ['', '', 'Twenty', 'Thirty', 'Forty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninety'];
            
            function convertHundreds(num) 
			{
                if (num === 0) 
					return '';

                let words = '';
                
                if (num >= 100) 
				{
//                    words += 'A' + num +' ';
                    words += ones[Math.floor(num / 100)] + ' Hundred ';
                    num %= 100;
                }
                
                if (num >= 20) 
				{
//                    words += 'B' + num +' ';
                    words += tens[Math.floor(num / 10)] + ' ';
                    num %= 10;
                }
                
                if (num > 0) 
				{
//                    words += 'C' + num +' ';
                    words += ones[num] + ' ';
                }
                
                return words.trim();
            }
            
            let result = '';
			if (dollars >=1000)
			{
				result += convertHundreds(Math.trunc(dollars/1000)) + ' Thousand '
			}
			
			result += convertHundreds(dollars%1000);
			
            // Add cents
            result += ' and ' + cents.toString().padStart(2, '0') + '/100';
            
            return result;
        }
    </script>
</body>
</html>