Skip to content

fix(printer): preserve IR receipt financial lines - #273

Draft
khaira777 wants to merge 9 commits into
mainfrom
fix/241-ir-thermal-receipt-safety
Draft

fix(printer): preserve IR receipt financial lines#273
khaira777 wants to merge 9 commits into
mainfrom
fix/241-ir-thermal-receipt-safety

Conversation

@khaira777

@khaira777 khaira777 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Intent

Implement the focused PR #273 fix so IR raw ESC/POS receipts preserve financial lines when fa-IR produces Persian digits or the textual ریال currency symbol, including large-amount and narrow-printer layout safety, without changing browser printing or general Persian support. Keep exact ریال-only normalization, do not broaden useUnicode semantics or make Rial/Toman UX decisions, preserve production aliases, avoid test-only production rewrites, and use no new testing framework. Verify the implementation thoroughly, then safely commit and push the feature branch.

What Changed

  • Routes non-browser tax-bill printing through raw ESC/POS-safe formatting while preserving the browser printing path.
  • Normalizes fa-IR’s textual ریال currency to IRR, uses Latin digits and safe spacing for raw receipts, and hardens narrow/large-amount financial layouts across frontend and backend templates.
  • Adds regression coverage for IR currency output, receipt widths, large amounts, browser formatting, and unsupported Persian text handling.

Risk Assessment

⚠️ Medium: The cross-path printer changes are bounded and satisfy the reviewed raw/browser currency, digit, and layout invariants, but they touch multiple receipt encoders and require the dedicated downstream verification phase.

Testing

Root and frontend locked dependencies were restored for testing, the focused suite passed with 332 assertions, and browser HTML evidence confirmed Persian ریال/digits without IRR leakage. No lint or full suite was run per phase constraints.

Evidence: Targeted printer suite output

> flo-desktop@3.0.5 test:printer
> ts-node --transpile-only -P tests/tsconfig.json tests/printer.test.ts

🧪 FloDesktop Printer Tests
============================================================

✅ Test 1: buildEscPos emits correct control bytes
   ✓ emits ESC @ (init)
   ✓ emits ESC a 1 (center)
   ✓ emits ESC a 0 (left)
   ✓ emits ESC E 1 (bold on)
   ✓ emits GS V 0 (full cut)
   ✓ emits LF after text lines
   ✓ contains visible "HEADER" text
   ✓ contains visible "plain line" text
   ✓ no stray {TOKEN} markers remain

✅ Test 1b: Unsupported receipt text is skipped with a warning
   ✓ skips unsupported Arabic line
   ✓ keeps the rest of the receipt printable
   ✓ reports the skipped store name

✅ Test 1c: ESC/POS output can be previewed without a printer
   ✓ paperless preview keeps receipt text
   ✓ paperless preview strips ESC/POS commands

✅ Test 2: Compact receipt (80mm, 48 cols)
[Printer] formatReceipt - template: compact
[Printer] formatReceipt - order: ORD-20260421-0001 bill: INV-20260421-0001
[Printer] formatReceipt - items count: 3 cols: 48
   ✓ renders business name
   ✓ renders bill number
   ✓ renders Cheeseburger row
   ✓ renders addon "Extra Cheese"
   ✓ renders addon "Bacon"
   ✓ renders special instruction
   ✓ renders subtotal ₹925.00
   ✓ renders discount line with negative sign
   ✓ renders tax total ₹40.00
   ✓ renders TOTAL with grand amount
   ✓ renders Cash payment
   ✓ renders UPI payment
   ✓ renders tax registration number
   ✓ renders non-configurable FloPOS footer
   ✓ long product name is truncated to fit
   ✓ ends with cut byte sequence
   ✓ item row columns are aligned (no smashed qty)
   ✓ item row right-edge total lines up at col 48
   ✓ item table has no redundant tax column

   — Rendered compact (80mm) —
────────────────────────────────────────────────
Flo Test Cafe
================================================
Bill #: INV-20260421-0001
Date: 4/21/2026 6:30:00 AM
Table: T3
------------------------------------------------
Item                              Qty     Amount
------------------------------------------------
Cheeseburger                      2      ₹540.00
  + Extra Cheese                          ₹20.00
  + Bacon                                 ₹20.00
  Note: No onions
Fresh Lime Soda                   1       ₹70.00
Very Long Product Name That Shou..3      ₹315.00
------------------------------------------------
Subtotal                                 ₹925.00
Discount                               -  ₹15.00
Tax                                       ₹40.00
TOTAL                                    ₹950.00
------------------------------------------------
Cash                                     ₹500.00
UPI                                      ₹450.00
================================================
42 MG Road, Bengaluru 560001
Ph: +91 98765 43210
Tax ID: TAXID-0001
Thank you!
Powered by FloPOS
https://flopos.com
────────────────────────────────────────────────

✅ Test 3: Compact receipt on 58mm paper (32 cols)
[Printer] formatReceipt - template: compact
[Printer] formatReceipt - order: ORD-20260421-0001 bill: INV-20260421-0001
[Printer] formatReceipt - items count: 3 cols: 32
   ✓ still renders business name
   ✓ still renders TOTAL
   ✓ no content line exceeds 32 cols

   — Rendered compact (58mm) —
────────────────────────────────
Flo Test Cafe
================================
Bill #: INV-20260421-0001
Date: 4/21/2026 6:30:00 AM
Table: T3
--------------------------------
Item              Qty     Amount
--------------------------------
Cheeseburger      2      ₹540.00
  + Extra Cheese          ₹20.00
  + Bacon                 ₹20.00
  Note: No onions
Fresh Lime Soda   1       ₹70.00
Very Long Produc..3      ₹315.00
--------------------------------
Subtotal                 ₹925.00
Discount               -  ₹15.00
Tax                       ₹40.00
TOTAL                    ₹950.00
--------------------------------
Cash                     ₹500.00
UPI                      ₹450.00
================================
42 MG Road, Bengaluru 560001
Ph: +91 98765 43210
Tax ID: TAXID-0001
Thank you!
Powered by FloPOS
https://flopos.com
────────────────────────────────

✅ Test 3b: Compact receipt on narrow 36-col printer
[Printer] formatReceipt - template: compact
[Printer] formatReceipt - order: ORD-20260421-0001 bill: INV-20260421-0001
[Printer] formatReceipt - items count: 3 cols: 36
   ✓ still renders TOTAL on 36-col printer
   ✓ keeps amount together on one line
   ✓ no content line exceeds 36 cols

   — Rendered compact (36 cols) —
────────────────────────────────────
Flo Test Cafe
====================================
Bill #: INV-20260421-0001
Date: 4/21/2026 6:30:00 AM
Table: T3
------------------------------------
Item                  Qty     Amount
------------------------------------
Cheeseburger          2      ₹540.00
  + Extra Cheese              ₹20.00
  + Bacon                     ₹20.00
  Note: No onions
Fresh Lime Soda       1       ₹70.00
Very Long Product Na..3      ₹315.00
------------------------------------
Subtotal                     ₹925.00
Discount                   -  ₹15.00
Tax                           ₹40.00
TOTAL                        ₹950.00
------------------------------------
Cash                         ₹500.00
UPI                          ₹450.00
====================================
42 MG Road, Bengaluru 560001
Ph: +91 98765 43210
Tax ID: TAXID-0001
Thank you!
Powered by FloPOS
https://flopos.com
────────────────────────────────────

✅ Test 3c: Narrow receipt reserves 3-char currency codes
[Printer] formatReceipt - template: compact
[Printer] formatReceipt - order: ORD-20260421-0001 bill: INV-20260421-0001
[Printer] formatReceipt - items count: 3 cols: 36
   ✓ renders USD amount without splitting currency code
   ✓ USD receipt has no line over 36 cols

✅ Test 3d: Trim decimals hides only trailing .00
[Printer] formatReceipt - template: compact
[Printer] formatReceipt - order: ORD-20260421-0001 bill: INV-20260421-0001
[Printer] formatReceipt - items count: 3 cols: 36
   ✓ trim decimals removes trailing .00 from whole amounts
[Printer] formatReceipt - template: compact
[Printer] formatReceipt - order: ORD-20260421-0001 bill: INV-20260421-0001
[Printer] formatReceipt - items count: 3 cols: 36
   ✓ trim decimals keeps non-zero decimals

✅ Test 4: Classic receipt template
[Printer] formatReceipt - template: classic
[Printer] formatReceipt - order: ORD-20260421-0001 bill: INV-20260421-0001
[Printer] formatReceipt - items count: 3 cols: 48
   ✓ renders business name
   ✓ renders item and total
   ✓ renders non-configurable FloPOS footer
   ✓ ends with cut

   — Rendered classic —
────────────────────────────────────────────────
Flo Test Cafe
------------------------------------------------
Invoice #: INV-20260421-0001
4/21/2026 6:30:00 AM
Table: T3
------------------------------------------------
Item                              Qty     Amount
------------------------------------------------
Cheeseburger                      2      ₹540.00
  + Extra Cheese                          ₹20.00
  + Bacon                                 ₹20.00
  Note: No onions
Fresh Lime Soda                   1       ₹70.00
Very Long Product Name That Shou..3      ₹315.00
------------------------------------------------
Discount                               -  ₹15.00
Subtotal                                 ₹925.00
Tax              

... [18877 bytes truncated] ...

in 32 columns
   ✓ [backend IR classic] preserves extreme IRR amount
[Printer] formatReceipt - template: detailed
[Printer] formatReceipt - order: ORD-IR-001 bill: INV-IR-001
[Printer] formatReceipt - items count: 1 cols: 32
   ✓ [backend IR detailed] keeps extreme IRR rows within 32 columns
   ✓ [backend IR detailed] preserves extreme IRR amount
[Printer] formatReceipt - template: compact
[Printer] formatReceipt - order: ORD-IR-001 bill: INV-IR-001
[Printer] formatReceipt - items count: 1 cols: 48
   ✓ backend free-form Persian text emits unsupported character warning
   ✓ [frontend compact unicode=false] preserves item line
   ✓ [frontend compact unicode=false] preserves item amount
   ✓ [frontend compact unicode=false] preserves Discount line
   ✓ [frontend compact unicode=false] preserves Tax line
   ✓ [frontend compact unicode=false] preserves TOTAL line
   ✓ [frontend compact unicode=false] preserves Cash line
   ✓ [frontend compact unicode=false] preserves IRR100,000.00
   ✓ [frontend compact unicode=false] preserves IRR10,000.00
   ✓ [frontend compact unicode=false] preserves IRR9,000.00
   ✓ [frontend compact unicode=false] preserves IRR99,000.00
   ✓ [frontend compact unicode=false] leaves zero warnings for numeric lines
   ✓ [frontend compact unicode=true] preserves item line
   ✓ [frontend compact unicode=true] preserves item amount
   ✓ [frontend compact unicode=true] preserves Discount line
   ✓ [frontend compact unicode=true] preserves Tax line
   ✓ [frontend compact unicode=true] preserves TOTAL line
   ✓ [frontend compact unicode=true] preserves Cash line
   ✓ [frontend compact unicode=true] preserves IRR100,000.00
   ✓ [frontend compact unicode=true] preserves IRR10,000.00
   ✓ [frontend compact unicode=true] preserves IRR9,000.00
   ✓ [frontend compact unicode=true] preserves IRR99,000.00
   ✓ [frontend compact unicode=true] leaves zero warnings for numeric lines
   ✓ [frontend classic unicode=false] preserves item line
   ✓ [frontend classic unicode=false] preserves item amount
   ✓ [frontend classic unicode=false] preserves Subtotal line
   ✓ [frontend classic unicode=false] preserves Discount line
   ✓ [frontend classic unicode=false] preserves Tax line
   ✓ [frontend classic unicode=false] preserves TOTAL line
   ✓ [frontend classic unicode=false] preserves Cash line
   ✓ [frontend classic unicode=false] preserves IRR100,000.00
   ✓ [frontend classic unicode=false] preserves IRR10,000.00
   ✓ [frontend classic unicode=false] preserves IRR9,000.00
   ✓ [frontend classic unicode=false] preserves IRR99,000.00
   ✓ [frontend classic unicode=false] leaves zero warnings for numeric lines
   ✓ [frontend classic unicode=true] preserves item line
   ✓ [frontend classic unicode=true] preserves item amount
   ✓ [frontend classic unicode=true] preserves Subtotal line
   ✓ [frontend classic unicode=true] preserves Discount line
   ✓ [frontend classic unicode=true] preserves Tax line
   ✓ [frontend classic unicode=true] preserves TOTAL line
   ✓ [frontend classic unicode=true] preserves Cash line
   ✓ [frontend classic unicode=true] preserves IRR100,000.00
   ✓ [frontend classic unicode=true] preserves IRR10,000.00
   ✓ [frontend classic unicode=true] preserves IRR9,000.00
   ✓ [frontend classic unicode=true] preserves IRR99,000.00
   ✓ [frontend classic unicode=true] leaves zero warnings for numeric lines
   ✓ [frontend detailed unicode=false] preserves item line
   ✓ [frontend detailed unicode=false] preserves item amount
   ✓ [frontend detailed unicode=false] preserves Subtotal (excl. tax) line
   ✓ [frontend detailed unicode=false] preserves Tax line
   ✓ [frontend detailed unicode=false] preserves TOTAL line
   ✓ [frontend detailed unicode=false] preserves Cash line
   ✓ [frontend detailed unicode=false] preserves IRR100,000.00
   ✓ [frontend detailed unicode=false] preserves IRR9,000.00
   ✓ [frontend detailed unicode=false] preserves IRR99,000.00
   ✓ [frontend detailed unicode=false] leaves zero warnings for numeric lines
   ✓ [frontend detailed unicode=true] preserves item line
   ✓ [frontend detailed unicode=true] preserves item amount
   ✓ [frontend detailed unicode=true] preserves Subtotal (excl. tax) line
   ✓ [frontend detailed unicode=true] preserves Tax line
   ✓ [frontend detailed unicode=true] preserves TOTAL line
   ✓ [frontend detailed unicode=true] preserves Cash line
   ✓ [frontend detailed unicode=true] preserves IRR100,000.00
   ✓ [frontend detailed unicode=true] preserves IRR9,000.00
   ✓ [frontend detailed unicode=true] preserves IRR99,000.00
   ✓ [frontend detailed unicode=true] leaves zero warnings for numeric lines
   ✓ [frontend tax-bill unicode=false] preserves item line
   ✓ [frontend tax-bill unicode=false] preserves item amount
   ✓ [frontend tax-bill unicode=false] preserves Subtotal line
   ✓ [frontend tax-bill unicode=false] preserves Discount line
   ✓ [frontend tax-bill unicode=false] preserves Total Tax line
   ✓ [frontend tax-bill unicode=false] preserves TOTAL line
   ✓ [frontend tax-bill unicode=false] preserves Cash line
   ✓ [frontend tax-bill unicode=false] preserves IRR100,000.00
   ✓ [frontend tax-bill unicode=false] preserves IRR10,000.00
   ✓ [frontend tax-bill unicode=false] preserves IRR9,000.00
   ✓ [frontend tax-bill unicode=false] preserves IRR99,000.00
   ✓ [frontend tax-bill unicode=false] leaves zero warnings for numeric lines
   ✓ [frontend tax-bill unicode=true] preserves item line
   ✓ [frontend tax-bill unicode=true] preserves item amount
   ✓ [frontend tax-bill unicode=true] preserves Subtotal line
   ✓ [frontend tax-bill unicode=true] preserves Discount line
   ✓ [frontend tax-bill unicode=true] preserves Total Tax line
   ✓ [frontend tax-bill unicode=true] preserves TOTAL line
   ✓ [frontend tax-bill unicode=true] preserves Cash line
   ✓ [frontend tax-bill unicode=true] preserves IRR100,000.00
   ✓ [frontend tax-bill unicode=true] preserves IRR10,000.00
   ✓ [frontend tax-bill unicode=true] preserves IRR9,000.00
   ✓ [frontend tax-bill unicode=true] preserves IRR99,000.00
   ✓ [frontend tax-bill unicode=true] leaves zero warnings for numeric lines
   ✓ [frontend classic unicode=false] keeps 58mm financial lines within width
   ✓ [frontend classic unicode=false] aligns 58mm header and item widths
   ✓ [frontend classic unicode=false] keeps narrow IRR amount
   ✓ [frontend classic unicode=false] has no narrow-layout warnings
   ✓ [frontend classic unicode=true] keeps 58mm financial lines within width
   ✓ [frontend classic unicode=true] aligns 58mm header and item widths
   ✓ [frontend classic unicode=true] keeps narrow IRR amount
   ✓ [frontend classic unicode=true] has no narrow-layout warnings
   ✓ frontend wide IRR rows stay within 42 columns
   ✓ frontend wide IRR amount remains intact
   ✓ frontend wide IRR rows stay within 42 columns
   ✓ frontend wide IRR amount remains intact
   ✓ browser printing preserves the Persian Rial symbol
   ✓ shared currency normalization leaves the Persian Rial symbol unchanged
   ✓ browser tax-bill printing preserves Persian Rial output
   ✓ browser tax-bill printing preserves Persian numeric output
   ✓ frontend free-form Persian text emits unsupported character warning
   ✓ hasUnsupportedPrinterChars("ریال") remains true before currency normalization
   ✓ hasUnsupportedPrinterChars("﷼") remains true
   ✓ hasUnsupportedPrinterChars("یار") remains true
   ✓ hasUnsupportedPrinterChars("کافه") remains true
   ✓ hasUnsupportedPrinterChars("IRR100,000.00") is false

✅ Test 12: Detect connected printers (hardware discovery)
   Found 1 printer(s):
     • HP_LaserJet_Pro_MFP_M127fn  [Unknown Thermal Printer, usb, idle, DEFAULT]
   ✓ detectConnectedPrinters returns an array
   ✓ host has at least one printer installed

   (skipping live print — pass --live or set FLO_LIVE_PRINT=1 to actually print)

============================================================
🏁 332 passed, 0 failed
Evidence: Browser path transcript

{"printMethod":"browser","hasPersianRial":true,"hasIrr":false,"hasPersianDigits":true,"containsGrandTotal":true,"printCallbackInvoked":true}

{"path":"/var/folders/y_/1ltcxtwj0zd_w1dg9jv4jl580000gn/T/no-mistakes-evidence/01KZWJKZXWZB9WRA06PXG4NRAX/browser-tax-bill.html","printMethod":"browser","htmlBytes":4761,"hasPersianRial":true,"hasIrr":false,"hasPersianDigits":true,"containsGrandTotal":true,"printCallbackInvoked":true}
Evidence: Rendered browser tax bill HTML

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Bill #INV-IR-001</title>
  <style>
    
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: 'Helvetica Neue', Arial, sans-serif; font-size: 12px; line-height: 1.4; color: #333; }
    .bill-container { max-width: 100%; margin: 0 auto; }
    .reprint-banner { text-align: center; font-size: 22px; font-weight: bold; letter-spacing: 2px; color: #c00; border: 3px solid #c00; padding: 6px; margin-bottom: 15px; }
    .header { text-align: center; margin-bottom: 20px; padding-bottom: 10px; border-bottom: 1px solid #ccc; }
    .header h1 { font-size: 24px; margin-bottom: 5px; }
    .bill-details { margin-bottom: 15px; }
    .bill-details table { width: 100%; }
    .items-table { width: 100%; border-collapse: collapse; margin-bottom: 15px; }
    .items-table th, .items-table td { padding: 8px; border-bottom: 1px solid #eee; text-align: left; }
    .items-table th { background: #f5f5f5; font-weight: bold; }
    .tax-table, .payments-table { width: 50%; margin-left: 50%; border-collapse: collapse; margin-bottom: 15px; }
    .tax-table th, .tax-table td, .payments-table th, .payments-table td { padding: 6px 8px; }
    .tax-table th, .payments-table th { background: #f9f9f9; text-align: left; }
    .totals-table { width: 100%; border-collapse: collapse; margin-bottom: 15px; }
    .totals-table td { padding: 6px 8px; }
    .total-row { border-top: 2px solid #333; font-size: 16px; }
    .footer { text-align: center; margin-top: 30px; padding-top: 15px; border-top: 1px solid #ccc; }
    .powered-by { font-size: 10px; margin-top: 8px; color: #555; }
    .text-right { text-align: right !important; }
    .text-muted { color: #666; }
    .text-italic { font-style: italic; color: #888; }
  
        .bill-container { padding: 5px; max-width: 58mm; font-size: 10px; }
        .header h1 { font-size: 14px; }
        .items-table th, .items-table td, .tax-table td, .totals-table td, .payments-table td { padding: 2px 4px; }
      
    @media print {
      .no-print { display: none !important; }
      body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
    }
  </style>
</head>
<body>
  <div class="bill-container">
    
    <!-- Header -->
    <div class="header">
      <h1>Flo Cafe Tehran</h1>
      
      
      
    </div>

    <!-- Bill Details -->
    <div class="bill-details">
      <table>
        <tr>
          <td><strong>Bill #:</strong> INV-IR-001</td>
          <td><strong>Date:</strong> ۱ اردیبهشت ۱۴۰۵، ۰۶:۳۰</td>
        </tr>
        
        
        
      </table>
    </div>

    <!-- Items Table -->
    <table class="items-table">
      <thead>
        <tr>
          <th>Item</th>
          <th class="text-right">Qty</th>
          <th class="text-right">Rate</th>
          <th class="text-right">Amount</th>
        </tr>
      </thead>
      <tbody>
        
          <tr>
            <td>
              Espresso
              
              
            </td>
            <td class="text-right">2</td>
            <td class="text-right">ریال۵۰٬۰۰۰٫۰۰</td>
            <td class="text-right">ریال۱۰۰٬۰۰۰٫۰۰</td>
          </tr>
        
      </tbody>
    </table>

    <!-- Tax Breakdown -->
    
    <table class="tax-table">
      <thead>
        <tr><th colspan="2">Tax Details</th></tr>
      </thead>
      <tbody>
        
          <tr><td>Tax</td><td class="text-right">ریال۹٬۰۰۰٫۰۰</td></tr>
        
      </tbody>
    </table>
    

    <!-- Totals -->
    <table class="totals-table">
      <tr><td>Subtotal</td><td class="text-right">ریال۱۰۰٬۰۰۰٫۰۰</td></tr>
      <tr><td>Discount</td><td class="text-right">-ریال۱۰٬۰۰۰٫۰۰</td></tr>
      <tr><td>Total Tax</td><td class="text-right">ریال۹٬۰۰۰٫۰۰</td></tr>
      
      
      <tr class="total-row"><td><strong>Grand Total</strong></td><td class="text-right"><strong>ریال۹۹٬۰۰۰٫۰۰</strong></td></tr>
    </table>

    <!-- Payments -->
    
    <table class="payments-table">
      <thead>
        <tr><th colspan="2">Payments</th></tr>
      </thead>
      <tbody>
        
          <tr><td>Cash</td><td class="text-right">ریال۹۹٬۰۰۰٫۰۰</td></tr>
        
      </tbody>
    </table>
    

    <!-- Footer -->
    <div class="footer">
      <p>Thank you for your visit!</p>
      <p>Tax included where applicable</p>
      <p class="powered-by">Powered by FloPOS<br>https://flopos.com</p>
    </div>
  </div>

  <div class="no-print" style="text-align:center;margin-top:20px;">
    <button onclick="window.print()" style="padding:10px 20px;font-size:16px;cursor:pointer;">Print Bill</button>
  </div>
</body>
</html>
  
- Outcome: 🔧 2 issues found → auto-fixed (2) ✅ across 3 runs (31m3s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 2 issues found → auto-fixed (2) ✅
  • 🚨 frontend/src/lib/printer/unicode.ts:41 - Required intent: “without changing browser printing.” Adding ریال to the shared normalizer changes web-print.ts behavior for IR with useUnicode=false: browser output now emits IRR instead of the previous ریال. Scope this normalization to raw ESC/POS paths.
  • 🚨 frontend/src/lib/printer/receipt-encoder.ts:125 - Required intent includes “large-amount and narrow-printer layout safety,” but the allocator floors nameWidth at 1 without enforcing the total width. At 42 columns, large IRR rate/amount values can make item rows and the Item header exceed printer width, causing wrapping or clipping.

🔧 Fix: Scoped Rial normalization and hardened narrow receipt layouts
2 errors still open:

  • 🚨 frontend/src/lib/printer/tax-bill-encoder.ts:78 - The required constraint is “without changing browser printing.” resolveEncoderCurrency now converts ریال to IRR, but printTaxBill can decode buildTaxBillBytes into browser HTML (usePrinter.ts:193-216), changing that browser path’s currency (and Latin-digit formatting). Keep browser rendering on a browser formatter or pass an explicit raw-ESC/POS mode.
  • 🚨 main/printers/thermal.ts:1120 - The required constraint includes “large-amount and narrow-printer layout safety.” On the production 32-column profile, a reachable amount such as IRR100,000,000,000,000.00 makes itemNameWidth equal 2; the fixed Item header then produces 34 columns, and Subtotal lines also exceed 32. Bound or wrap oversized financial values at the shared row-formatting boundary while preserving their full text.

🔧 Fix: Scoped browser tax formatting and bounded backend financial rows
✅ Re-checked - no issues remain.

🔧 **Test** - 2 issues found → auto-fixed (2) ✅
  • ⚠️ tests/printer.test.ts:816 - The wide-layout assertion checks every decoded line; the fa-IR date line produces replacement glyphs longer than 42 columns, while the financial lines remain width-safe. Narrow the assertion to the intended financial/output rows.
  • 🚨 frontend/src/hooks/usePrinter.ts:216 - The changed tax-bill browser fallback decodes ESC/POS bytes into HTML, causing fa-IR currency and numerals to become '?'/'�'. The dedicated HTML browser path passes, but this fallback does not preserve browser output.
  • npm run test:printer
  • npm run test:receipt-column-width
  • Manual backend/frontend IR raw ESC/POS rendering for large amounts and narrow widths
  • Manual browser HTML and tax-bill fallback output checks

🔧 Fix: Scoped IRR layout assertions; browser fallback remains unresolved
1 error still open:

  • 🚨 frontend/src/hooks/usePrinter.ts:216 - Browser tax-bill fallback output does not preserve Persian ریال or fa-IR digits; the generated decoded payload contains malformed/control output and replacement glyphs.
  • npm run test:printer with test_exit_code=1
  • Direct buildTaxBillBytes(..., { rawEscPos: false }) reproduction for an IR/IRR bill
  • Rendered receipt preview inspection from the focused test output
  • Worktree cleanup and git status --short --branch

🔧 Fix: Route tax bill browser printing through safe HTML formatter
✅ Re-checked - no issues remain.

  • npm run test:printer
  • Direct usePrinterStore.printTaxBill browser-mode harness
  • git diff --check ae4c31e... 2c3bb5a...
  • Verified transient dependency cleanup and clean worktree
✅ **Document** - passed

✅ No issues found.

🔧 **Lint** - 1 issue found → auto-fixed ✅
  • ⚠️ package.json:17 - Configured ESLint checks could not run because repository dependencies are unavailable (eslint and @typescript-eslint/eslint-plugin are missing).

🔧 Fix: Fix receipt layout lint warning
✅ Re-checked - no issues remain.

✅ **Push** - passed

✅ No issues found.

Ensures numbers in thermal print formatters force Latin digits via -u-nu-latn
and clean non-breaking spaces. Adds safe IRR symbol fallback in printer unicode maps so raw ESC/POS lines are not dropped when business locale is IR.

Refs #241
@khaira777 khaira777 changed the title fix(printing): preserve IR receipt financial lines fix(printer): preserve IR receipt financial lines Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant