Skip to content

feat(print): add 76mm impact printer support - #1502

Open
khaira777 wants to merge 6 commits into
mainfrom
fm/flocafe-impact-printer-support
Open

khaira777 wants to merge 6 commits into
mainfrom
fm/flocafe-impact-printer-support

Conversation

@khaira777

@khaira777 khaira777 commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1481

Intent

Implement native 76mm impact printer support (EPSON TM-U220D, Bixolon SRP-275, Xprinter XP-76, etc.) and capability-based printer onboarding across FloCafe:

  1. Add the escpos-impact-76 printer profile (cols-32, LATIN_THERMAL_CAPABILITIES, raster.enabled: false, cutMode: 'partial', fontAColumns: 32, fontBColumns: 40, printWidthMm: 64) before epson-tm-series in main/printers/profiles.ts, and update epson-tm-series aliases so TM-U* impact, TM-L* label, and EcoTank printers are never hijacked as 48-column thermal raster printers.
  2. Expand fallback transliteration in normalizeThermalText (shared/print/thermal-capabilities.ts) when no native codepage can represent the text: keep existing LATIN_ASCII_MAP entries byte-identical, then apply multi-char/stroke Latin foldings (Æ/æ -> AE/ae, Œ/œ -> OE/oe, Ø/ø -> O/o, Ł/ł -> L/l, Đ/đ -> D/d, Þ/þ -> TH/th, Ğ/ğ -> G/g, İ/ı -> I/i, Ş/ş -> S/s) followed by Unicode NFD combining-mark stripping (\p{Diacritic}) on Latin-script characters.
  3. Guard unconditional GS v 0 logo ({RAW_BYTES:...}) and GS ( k QR code emissions with rasterCapabilityEnabled(capabilities) in main/printers/document-classic.ts, main/printers/document-compact.ts, frontend/src/lib/printer/receipt-encoder.ts, and main/printers/thermal.ts (printPaymentTicket), while keeping the centered FLOCAFE-TICKET:<order_number> text line.
  4. Fix OS printer detection in main/printers/thermal.ts (getMacOSPrinterDetails and detectWindowsMakeModel) so tm-u/u220 maps to make: 'Epson', model: 'TM-U Series' (cols-32) and non-TM Epson printers (EcoTank, ET-, L3, L4, WorkForce, WF-, Stylus) are not classified as TM Series thermal; add compatibility: 'escpos_thermal' | 'escpos_impact' | 'driver_only' to PrinterInfo via classifyDetectedPrinterCompatibility(info) (keeping driver_only queues visible in Settings -> Printers with a warning badge and a 1-click switch to Browser print, including Star TSP100/TSP143/SP700/mC-Print, Inkjet/Laser, Label, and PDF queues).
  5. Add migration 125 (add_printer_profile_id) in main/db.ts (profile_id TEXT DEFAULT NULL on printers), persist and validate profile_id in main/routes/printers.ts (POST /api/printers and PUT /api/printers/:id, returning configured_profile_id alongside resolved profile_id), add the Printer Type / Profile selector and driver_only warning badge + 1-click Browser switch in frontend/src/components/settings/PrintersSettingsTab.tsx, add the translation keys across all 24 locale files and messages.d.ts, update docs/architecture/printing.md and docs/guides/testing-printers.md, update tests/receipt-column-oracle.test.ts + golden fixture, and add tests/impact-printer-support.test.ts wired into package.json.

What Changed

  • Add the escpos-impact-76 32-column printer profile (raster.enabled: false, cutMode: 'partial', printWidthMm: 64) in main/printers/profiles.ts, refine epson-tm-series aliases and OS printer detection in main/printers/thermal.ts so TM-U* impact, TM-L* label, and non-TM Epson queues are not misclassified as 48-column thermal raster printers, and classify detected queues as escpos_thermal, escpos_impact, or driver_only.
  • Guard ESC/POS GS v 0 raster logo and GS ( k QR code emissions with rasterCapabilityEnabled(capabilities) across backend and frontend receipt/ticket encoders while preserving the text ticket fallback, and expand normalizeThermalText in shared/print/thermal-capabilities.ts with extended Latin stroke/ligature foldings and Unicode NFD diacritic stripping when no native codepage can encode the text and raster fallback does not intercept it.
  • Add schema migration 125 (add_printer_profile_id) and profile_id validation/persistence in main/routes/printers.ts, add the printer profile selector plus driver_only warning badge and one-click Browser print switch in frontend/src/components/settings/PrintersSettingsTab.tsx with translations across all 24 locales, and update documentation and test suites.

Risk Assessment

✅ Low: The changes cleanly introduce the escpos-impact-76 printer profile, extend Latin fallback transliteration for non-raster profiles without altering native codepage representability or raster-capable thermal behavior, guard GS v 0 logos and GS ( k payment-ticket QR codes on rasterCapabilityEnabled, classify detected OS printers by ESC/POS compatibility, and persist/validate printers.profile_id via additive migration v125 with comprehensive unit, upgrade-path, oracle, and E2E test coverage.

Testing

Ran targeted backend tests (test:impact-printer, test:receipt-column-oracle, test:script-coverage, test:upgrade-path, test:backup, test:raster), translation and documentation verifiers (i18n:check, docs:check), and the live Playwright Chromium E2E suite (frontend/e2e/qa-settings-receipts-printers.spec.ts), capturing 7 screenshots and 1 rendered receipt HTML artifact in the evidence directory; all checks and live scenarios passed.

  • Live validation: ✅ go - 5 of 5 scenarios driven live against the product
Scenario Result Live Evidence
76mm impact printers (EPSON TM-U220/U220D/U230, Bixolon SRP-275, Xprinter XP-76) resolve to escpos-impact-76 (cols-32, raster.enabled: false, cutMode: partial, fontAColumns: 32, fontBColumns: 40, prin… ✅ pass live npm run test:impact-printer and npm run test:receipt-column-oracle
Fallback Latin transliteration in normalizeThermalText preserves existing LATIN_ASCII_MAP entries byte-identical, folds extended multi-char/stroke/dotless/horned Latin characters (Æ/æ, Œ/œ, Ø/ø, Ł/ł,… ✅ pass live npm run test:impact-printer
Classic/compact receipt encoders (backend and frontend) and payment tickets suppress GS v 0 raster logo bytes and GS ( k QR graphics when capabilities.raster.enabled is false while preserving centered… ✅ pass live npm run test:impact-printer and npm run test:raster
OS printer detection classifies TM-U/U220 queues as Epson TM-U Series (cols-32, escpos_impact) and non-ESC/POS queues (EcoTank, Star TSP100/TSP143/SP700/mC-Print, Inkjet/Laser, Label, PDF) as driver_o… ✅ pass live npm run test:impact-printer, 01_detected_printers_impact_and_driver_only.png, and 02_switched_to_browser_print.png
Database migration 125 (add_printer_profile_id) upgrades cleanly and preserves backups, Settings -> Printers allows selecting the 76mm Impact profile (auto-defaulting Printable Columns to cols-32 and… ✅ pass live npm run test:impact-printer, npm run test:upgrade-path, npm run test:backup, 03_add_printer_impact_profile_form.png, and 04_configured_impact_printer_card.png

Detected printers showing 76mm Impact badge and Driver / Browser print only warnings
1-click Use Browser Print switch toast and persisted Print Method selection
Add Printer form with 76mm Impact profile selected and 32-column auto-default + guidance hint
Configured EPSON TM-U220D Kitchen printer card showing 32 columns and Epson TM-U220 / 76mm Impact profile
Settings Print Method ESC/POS guidance hint
Configured XP-58 SIR printer card with Extra Large text size

Evidence: Live rendered Browser Print HTML popup for 58mm receipt
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <title>Bill # INV-20261108-0001</title>
  <style>
    
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: -apple-system, 'Segoe UI', Tahoma, 'Noto Naskh Arabic', 'Noto Sans Bengali', 'Vrinda', 'Bangla Sangam MN', 'Noto Sans Devanagari', 'Nirmala UI', 'Kohinoor Devanagari', 'Devanagari Sangam MN', 'Noto Sans Gurmukhi', 'Raavi', 'Kohinoor Gurmukhi', 'Gurmukhi Sangam MN', 'Noto Sans Thai', 'Leelawadee UI', Thonburi, 'PingFang SC', 'Microsoft YaHei', 'Noto Sans CJK SC', 'Hiragino Sans', 'Yu Gothic', 'Meiryo', 'Noto Sans CJK JP', 'Noto Sans JP', 'Helvetica Neue', Arial, sans-serif; font-size: 13px; font-weight: 600; line-height: 1.4; color: #000; }
    body:lang(zh-TW) { font-family: -apple-system, 'Segoe UI', Tahoma, 'Noto Naskh Arabic', 'PingFang TC', 'Microsoft JhengHei', 'Noto Sans CJK TC', 'Noto Sans TC', 'Helvetica Neue', Arial, sans-serif; }
    .bill-container { max-width: 100%; margin: 0 auto; color: #000; }
    .reprint-banner { text-align: center; font-size: 20px; font-weight: 700; letter-spacing: 2px; color: #000; border: 2px solid #000; padding: 6px; margin-bottom: 12px; }
    .online-order-banner { text-align: center; font-size: 18px; font-weight: 700; letter-spacing: 1px; color: #000; border: 2px solid #000; padding: 6px; margin-bottom: 12px; }
    .online-order-banner .online-order-detail { font-size: 13px; font-weight: 600; letter-spacing: normal; margin-top: 2px; color: #000; }
    .header { text-align: center; margin-bottom: 12px; padding-bottom: 8px; color: #000; border-bottom: 1px solid #000; }
    .receipt-logo { max-width: 80%; max-height: 120px; object-fit: contain; margin: 0 auto 8px auto; display: block; }
    .header h1 { font-size: 20px; font-weight: 700; margin-bottom: 4px; color: #000; }
    .bill-details { margin-bottom: 12px; color: #000; }
    .bill-details table { width: 100%; }
    .items-table { width: 100%; border-collapse: collapse; margin-bottom: 12px; color: #000; }
    .items-table th, .items-table td { padding: 6px; border-bottom: 1px solid #000; text-align: start; color: #000; }
    .items-table th { background: transparent; font-weight: 700; color: #000; }
    .tax-table, .payments-table { width: 50%; margin-inline-start: 50%; border-collapse: collapse; margin-bottom: 12px; color: #000; }
    .tax-table th, .tax-table td, .payments-table th, .payments-table td { padding: 4px 6px; color: #000; }
    .tax-table th, .payments-table th { background: transparent; text-align: start; font-weight: 700; color: #000; }
    .totals-table { width: 100%; border-collapse: collapse; margin-bottom: 12px; color: #000; }
    .totals-table td { padding: 4px 6px; color: #000; }
    .total-row { border-top: 2px solid #000; font-size: 16px; font-weight: 700; color: #000; }
    .footer { text-align: center; margin-top: 16px; padding-top: 10px; border-top: 1px solid #000; color: #000; }
    .powered-by { font-size: 11px; font-weight: 600; margin-top: 8px; color: #000; }
    .text-end { text-align: end !important; }
    .num { unicode-bidi: isolate; white-space: nowrap; }
    .ltr { direction: ltr; unicode-bidi: isolate; }
    .text-muted { color: #000; font-weight: 600; }
    .text-italic { font-style: italic; color: #000; font-weight: 600; }
  
        @page { size: 58mm auto; margin: 0; }
        body { color: #000; font-size: 17px; font-weight: 700; width: 58mm; max-width: 58mm; margin: 0 auto; }
        .bill-container { padding: 2mm; width: 100%; max-width: 58mm; font-size: 17px; font-weight: 700; color: #000; }
        .reprint-banner, .online-order-banner { color: #000; border: 2px solid #000; font-size: 20px; font-weight: 700; }
        .header { color: #000; border-bottom: 1px solid #000; }
        .header h1 { font-size: 21px; font-weight: 700; color: #000; }
        .text-muted, .text-italic { color: #000; font-weight: 700; }
        .items-table th, .tax-table th, .payments-table th { background: transparent; color: #000; border-bottom: 1px solid #000; font-weight: 700; }
        .items-table td { color: #000; border-bottom: 1px dashed #000; font-size: 17px; font-weight: 700; }
        .total-row { color: #000; border-top: 2px solid #000; font-size: 19px; font-weight: 700; }
        .footer { color: #000; border-top: 1px solid #000; }
        .powered-by { color: #000; font-size: 13px; font-weight: 600; }
        .tax-table, .payments-table { width: 100%; margin-inline-start: 0; }
        .items-table th, .items-table td, .tax-table td, .totals-table td, .payments-table td { padding: 2px 4px; }
      
        .bill-container { overflow-wrap: anywhere; word-break: break-word; }
        .items-table thead, .items-table tbody { display: block; width: 100%; }
        .items-table thead tr, .items-table tbody tr { display: flex; flex-wrap: wrap; width: 100%; border-bottom: 1px dashed #000; }
        .items-table thead tr { border-bottom: 1px solid #000; }
        .items-table th, .items-table td { border-bottom: none; overflow-wrap: anywhere; word-break: break-word; }
        .items-table th:first-child, .items-table td:first-child { flex: 1 1 100%; width: 100%; max-width: 100%; }
        .items-table th:not(:first-child), .items-table td:not(:first-child) { flex: 1 1 auto; min-width: 0; }
        .items-table td.num { white-space: normal; overflow-wrap: anywhere; word-break: break-word; }
      
    @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>Store</h1>
      
      
      
    </div>

    <!-- Bill Details -->
    <div class="bill-details">
      <table>
        <tr>
          <td><strong>Bill #</strong> INV-20261108-0001</td>
          <td class="text-end"><strong>Date</strong> Nov 8, 2569 BE, 01:15 AM</td>
        </tr>
        
        
        
        
        
      </table>
    </div>

    <!-- Items Table -->
    <table class="items-table">
      <thead>
        <tr>
          <th>Item</th>
          <th class="text-end">Qty</th>
          <th class="text-end">Rate</th>
          <th class="text-end">Amount</th>
        </tr>
      </thead>
      <tbody>
        
          <tr>
            <td>
              E2E Coffee
              
              
            </td>
            <td class="text-end num">2</td>
            <td class="text-end num">฿60.00</td>
            <td class="text-end num">฿128.40</td>
          </tr>
        
      </tbody>
    </table>

    <!-- Tax Breakdown -->
    
    <table class="tax-table">
      <thead>
        <tr><th colspan="2">Tax Details</th></tr>
      </thead>
      <tbody>
        
          <tr><td>Tax @7%</td><td class="text-end num">฿8.40</td></tr>
        
      </tbody>
    </table>
    

    <!-- Totals -->
    <table class="totals-table">
      
      
      <tr><td>Subtotal</td><td class="text-end num">฿120.00</td></tr>
      
      
      
      
      
      
      <tr class="total-row"><td><strong>Grand Total</strong></td><td class="text-end num"><strong>฿128.40</strong></td></tr>
      
      
      
    </table>

    <!-- Payments -->
    

    <!-- Footer -->
    <div class="footer">
      <p>Thank you for your visit!</p>
      <p>Tax included where applicable</p>
      <p class="powered-by">Powered by FloPOS (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>
  
![Live rendered Browser Print 58mm receipt preview screenshot](https://github.com/user-attachments/assets/568d466b-422f-4b7e-9137-f3e681e27b4b)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 5 of 5 scenarios driven live against the product
Scenario Result Live Evidence
76mm impact printers (EPSON TM-U220/U220D/U230, Bixolon SRP-275, Xprinter XP-76) resolve to escpos-impact-76 (cols-32, raster.enabled: false, cutMode: partial, fontAColumns: 32, fontBColumns: 40, prin… ✅ pass live npm run test:impact-printer and npm run test:receipt-column-oracle
Fallback Latin transliteration in normalizeThermalText preserves existing LATIN_ASCII_MAP entries byte-identical, folds extended multi-char/stroke/dotless/horned Latin characters (Æ/æ, Œ/œ, Ø/ø, Ł/ł,… ✅ pass live npm run test:impact-printer
Classic/compact receipt encoders (backend and frontend) and payment tickets suppress GS v 0 raster logo bytes and GS ( k QR graphics when capabilities.raster.enabled is false while preserving centered… ✅ pass live npm run test:impact-printer and npm run test:raster
OS printer detection classifies TM-U/U220 queues as Epson TM-U Series (cols-32, escpos_impact) and non-ESC/POS queues (EcoTank, Star TSP100/TSP143/SP700/mC-Print, Inkjet/Laser, Label, PDF) as driver_o… ✅ pass live npm run test:impact-printer, 01_detected_printers_impact_and_driver_only.png, and 02_switched_to_browser_print.png
Database migration 125 (add_printer_profile_id) upgrades cleanly and preserves backups, Settings -> Printers allows selecting the 76mm Impact profile (auto-defaulting Printable Columns to cols-32 and… ✅ pass live npm run test:impact-printer, npm run test:upgrade-path, npm run test:backup, 03_add_printer_impact_profile_form.png, and 04_configured_impact_printer_card.png
  • npm run test:impact-printer
  • npm run test:receipt-column-oracle
  • npm run test:script-coverage
  • npm run i18n:check
  • npm run docs:check
  • npm run test:upgrade-path
  • npm run test:backup
  • npm run test:raster
  • npm run build:frontend && EVIDENCE_DIR=~/.no-mistakes/evidence/01M6VJVXH3SYD30JNV7HFR80BE npx --prefix frontend playwright test --config=frontend/playwright.config.ts --project=chromium frontend/e2e/qa-settings-receipts-printers.spec.ts
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@coderabbitai

coderabbitai Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add 76mm impact printing and capability-based printer onboarding

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Support 76mm impact printers with 32-column text and no unsupported raster graphics.
Diagram

graph TD
  OS["OS Queues"] --> Classifier["Compatibility Classification"] --> Settings["Printer Settings"] --> API["Printer API"] --> DB[("Printer Records")]
  DB --> Profiles["Capability Profiles"] --> Renderers["Receipt Renderers"]
  Settings --> Browser["Browser Print"]
Loading
High-Level Assessment

Use the existing profile and capability system with an explicit override: it carries impact constraints through established print paths without introducing a separate renderer. Probing unknown OS queues with raw commands was considered less safe than identifying driver-only devices and offering Browser Print.

Files changed (50) +1887 / -219

Enhancement (9) +451 / -116
PrintersSettingsTab.tsxAdd profile selection and compatibility guidance +99/-5

Add profile selection and compatibility guidance

• Adds explicit profile selection with width defaults, preserves manual width choices, and shows impact and driver-only guidance for detected queues. Driver-only queues offer a Browser Print switch rather than quick-add.

frontend/src/components/settings/PrintersSettingsTab.tsx

usePrinter.tsExpose configured profile on printer type +1/-0

Expose configured profile on printer type

• Adds the optional configured_profile_id field to the frontend hardware-printer contract.

frontend/src/hooks/usePrinter.ts

en.jsonDefine English printer onboarding messages +14/-1

Define English printer onboarding messages

• Adds profile names, impact guidance, compatibility labels, warnings, and Browser Print action text.

frontend/src/lib/i18n/messages/en.json

electron.d.tsExpose printer profile fields in Electron types +2/-0

Expose printer profile fields in Electron types

• Adds optional resolved and configured profile identifiers to the Electron printer interface.

frontend/src/types/electron.d.ts

db.tsPersist optional printer profile overrides +10/-0

Persist optional printer profile overrides

• Migration 125 adds printers.profile_id to existing databases, and the fresh schema includes the nullable column.

main/db.ts

profiles.tsIntroduce the 76mm impact profile +99/-13

Introduce the 76mm impact profile

• Adds a 32-column, partial-cut impact profile with raster disabled and narrows thermal-model aliases to avoid matching impact and non-receipt Epson printers. Also exposes profile-ID validation.

main/printers/profiles.ts

thermal.tsClassify OS queues and respect ticket capabilities +157/-91

Classify OS queues and respect ticket capabilities

• Improves macOS and Windows make/model detection, annotates detected queues as thermal, impact, or driver-only, and gives impact queues a 32-column width. Payment tickets omit native QR bytes without raster capability while retaining the ticket text.

main/printers/thermal.ts

printers.tsValidate and return configured printer profiles +31/-4

Validate and return configured printer profiles

• Create and update routes validate and persist optional profile IDs; responses distinguish the configured ID from the resolved profile. New printers can inherit the selected profile's paper width.

main/routes/printers.ts

thermal-capabilities.tsExpand Latin fallback transliteration +38/-2

Expand Latin fallback transliteration

• Preserves existing ASCII mappings while adding Latin ligature and stroke substitutions followed by diacritic stripping when no native code page represents the text.

shared/print/thermal-capabilities.ts

Bug fix (3) +19 / -5
receipt-encoder.tsSuppress unsupported frontend receipt logos +9/-3

Suppress unsupported frontend receipt logos

• Classic and compact receipt encoders omit raster logos when supplied printer capabilities disable raster output.

frontend/src/lib/printer/receipt-encoder.ts

document-classic.tsGuard classic bill raster logos +5/-1

Guard classic bill raster logos

• Skips raw logo bitmap commands when the printer's supplied capabilities disable raster output.

main/printers/document-classic.ts

document-compact.tsGuard compact bill raster logos +5/-1

Guard compact bill raster logos

• Skips raw logo bitmap commands when the printer's supplied capabilities disable raster output.

main/printers/document-compact.ts

Tests (6) +965 / -4
qa-settings-receipts-printers.spec.tsExercise impact and driver-only setup in Settings +203/-2

Exercise impact and driver-only setup in Settings

• Adjusts form selectors and adds an onboarding scenario covering detection badges, the Browser Print switch, profile defaults, persistence, and invalid IDs.

frontend/e2e/qa-settings-receipts-printers.spec.ts

package.jsonRegister impact printer regression tests +2/-1

Register impact printer regression tests

• Adds a dedicated impact-printer test command and includes it in the full test suite.

package.json

impact-printer-support.test.tsCover impact printing across the stack +693/-0

Cover impact printing across the stack

• Tests profile matching, transliteration, raster and QR suppression, OS compatibility, migration registration, and profile-ID API validation and persistence.

tests/impact-printer-support.test.ts

raster-printing.test.tsAccount for non-raster impact capability +5/-1

Account for non-raster impact capability

• Updates profile capability expectations so the impact profile must disable raster while thermal profiles retain it.

tests/raster-printing.test.ts

receipt-column-oracle.test.tsInclude impact profile in column oracle +1/-0

Include impact profile in column oracle

• Registers the impact profile's 32-column width in receipt column expectations.

tests/receipt-column-oracle.test.ts

upgrade-path.test.tsVerify the v125 printer migration +61/-0

Verify the v125 printer migration

• Upgrades a v124-style printer row and checks that profile_id is added as NULL without losing the row or introducing schema-health findings.

tests/upgrade-path.test.ts

Documentation (32) +452 / -94
printing.mdDocument impact capabilities and profile resolution +29/-15

Document impact capabilities and profile resolution

• Describes the fifth printer profile, persisted profile overrides, compatibility classification, and capability-gated logos and payment-ticket QR codes.

docs/architecture/printing.md

testing-printers.mdClarify how to verify resolved profiles +9/-11

Clarify how to verify resolved profiles

• Directs testers to the profile reference and explains how configured and resolved profile IDs relate.

docs/guides/testing-printers.md

printers.mdExplain impact setup and driver-only onboarding +15/-21

Explain impact setup and driver-only onboarding

• Documents the profile selector, 32-column impact behavior, and Browser Print guidance for incompatible OS queues.

docs/printers.md

api.mdDocument printer profile API fields +3/-3

Document printer profile API fields

• Adds profile_id to printer create and update requests and notes that payment-ticket QR output depends on raster capability.

docs/reference/api.md

translation-terminology.mdDocument Latin fallback transliteration +18/-17

Document Latin fallback transliteration

• Distinguishes generic ASCII folding from Latin-profile native code pages and expanded fallback transliteration.

docs/reference/translation-terminology.md

ar.jsonTranslate printer onboarding into Arabic +14/-1

Translate printer onboarding into Arabic

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/ar.json

bn.jsonTranslate printer onboarding into Bengali +14/-1

Translate printer onboarding into Bengali

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/bn.json

de.jsonTranslate printer onboarding into German +14/-1

Translate printer onboarding into German

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/de.json

es.jsonTranslate printer onboarding into Spanish +14/-1

Translate printer onboarding into Spanish

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/es.json

fa.jsonTranslate printer onboarding into Persian +14/-1

Translate printer onboarding into Persian

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/fa.json

fil.jsonTranslate printer onboarding into Filipino +14/-1

Translate printer onboarding into Filipino

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/fil.json

fr.jsonTranslate printer onboarding into French +14/-1

Translate printer onboarding into French

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/fr.json

ha.jsonTranslate printer onboarding into Hausa +14/-1

Translate printer onboarding into Hausa

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/ha.json

hi.jsonTranslate printer onboarding into Hindi +14/-1

Translate printer onboarding into Hindi

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/hi.json

id.jsonTranslate printer onboarding into Indonesian +14/-1

Translate printer onboarding into Indonesian

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/id.json

it.jsonTranslate printer onboarding into Italian +14/-1

Translate printer onboarding into Italian

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/it.json

ja.jsonTranslate printer onboarding into Japanese +14/-1

Translate printer onboarding into Japanese

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/ja.json

jv.jsonTranslate printer onboarding into Javanese +14/-1

Translate printer onboarding into Javanese

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/jv.json

ko.jsonTranslate printer onboarding into Korean +14/-1

Translate printer onboarding into Korean

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/ko.json

mr.jsonTranslate printer onboarding into Marathi +14/-1

Translate printer onboarding into Marathi

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/mr.json

ne.jsonTranslate printer onboarding into Nepali +14/-1

Translate printer onboarding into Nepali

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/ne.json

nl.jsonTranslate printer onboarding into Dutch +14/-1

Translate printer onboarding into Dutch

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/nl.json

pt.jsonTranslate printer onboarding into Portuguese +14/-1

Translate printer onboarding into Portuguese

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/pt.json

ru.jsonTranslate printer onboarding into Russian +14/-1

Translate printer onboarding into Russian

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/ru.json

sq.jsonTranslate printer onboarding into Albanian +14/-1

Translate printer onboarding into Albanian

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/sq.json

sw.jsonTranslate printer onboarding into Swahili +14/-1

Translate printer onboarding into Swahili

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/sw.json

th.jsonTranslate printer onboarding into Thai +14/-1

Translate printer onboarding into Thai

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/th.json

tr.jsonTranslate printer onboarding into Turkish +14/-1

Translate printer onboarding into Turkish

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/tr.json

ur.jsonTranslate printer onboarding into Urdu +14/-1

Translate printer onboarding into Urdu

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/ur.json

vi.jsonTranslate printer onboarding into Vietnamese +14/-1

Translate printer onboarding into Vietnamese

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/vi.json

zh-tw.jsonTranslate printer onboarding into Traditional Chinese +14/-1

Translate printer onboarding into Traditional Chinese

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/zh-tw.json

zh.jsonTranslate printer onboarding into Simplified Chinese +14/-1

Translate printer onboarding into Simplified Chinese

• Adds localized profile options, impact guidance, compatibility labels, and Browser Print actions.

frontend/src/lib/i18n/messages/zh.json

@qodo-code-review

qodo-code-review Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Thermal receipts lose Vietnamese/Turkish accents ✓ Resolved

Resolving commit: 0f8a899

Resolution reason
The commit restructures shared/print/thermal-capabilities.ts: transliterateThermalText now returns
text unchanged when transliteration is disabled or when a native code page can represent the Latin
text. A new normalizeThermalText returns the text untouched when raster.enabled is true and the text
is not representable, so raster-capable printers keep raster rendering for strings like Tiếng Việt.
The extended map additions (Ơ/ơ/Ư/ư) remain, but the raster-capable path is preserved. A test
asserts Tiếng Việt and Đơn vị are non-representable and preserved on epson-tm-series. The approach
differs from the suggested fix (gating on raster inside the fold) but addresses the same regression.
Description
transliterateThermalText now folds every Latin string that no single native code page can
represent (extended map plus NFD \p{Diacritic} stripping). The fold runs for every profile using
LATIN_THERMAL_CAPABILITIES, not only the impact profile, and isThermalTextRepresentable calls
the same function. So on raster-enabled Epson TM and Xprinter thermal printers, strings like `Đơn
vị, Tiếng Việt, ş or ł are now judged representable and printed as ASCII text (Don vi`),
when before they were sent to the raster band and printed with correct diacritics.


Remediation recommended

2. Existing Epson TM printers fall back to generic ✓ Resolved

Resolving commit: 0f8a899

Resolution reason
Restored broad 'epson tm' and spaced/model-specific aliases for epson-tm-series, added
TM-T100/TM-H600/TM-H6000 aliases, excluded TM-U and non-TM Epson (EcoTank, WorkForce, Stylus,
L-series) from name matching, widened thermal detection and model extraction for TM variants, and
added regression tests for both resolution and classification.
Description
The epson-tm-series aliases replace the broad 'epson tm' token with model-specific tokens that
miss names such as EPSON TM Receipt, space-separated EPSON TM T88V, EPSON TM-H6000IV,
TM-T100, and detected strings such as TM Series or TM-H600. When an existing printer has no
explicit profile_id, resolvePrinterProfile falls back to generic-escpos-80, changing its
encoding, cut mode, and default width; detection also classifies some of these queues as
driver-only.

3. Impact setup guide omits 76 mm ✗ Dismissed
Description
FloCafe adds the escpos-impact-76 profile with a 32-column default, but the docs.flopos.com
operator guide still tells users to choose only 58 mm or 80 mm paper. Operators following that guide
when adding a TM-U220 or SRP-275 are not directed to the new profile and may select the wrong width.

4. Vietnamese impact tickets fail to print ✓ Resolved

Resolving commit: 0f8a899

Resolution reason
In shared/print/thermal-capabilities.ts, LATIN_EXTENDED_ASCII_MAP gained explicit foldings Ơ→O, ơ→o,
Ư→U, ư→u (the suggested fix's explicit mapping). The transliteration path was restructured so the
Latin fallback runs before the representability check, and a new normalizeThermalText path is used.
A new test in tests/impact-printer-support.test.ts asserts 'Cơm Sườn Nướng & Phở Bò Ơi Ướp' folds to
ASCII on escpos-impact-76, and that a priced Vietnamese item 'Cơm Sườn Nướng' appears folded on the
impact payment ticket. This addresses the finding with the same explicit-mapping approach, though
the file restructuring differs from the suggested prompt's wording.
Description
The new Latin fallback strips combining marks but does not fold horned vowels Ơ/ơ and Ư/ư, which
NFD leaves as horned vowels. On the new raster-disabled impact profile, a priced item such as Sườn
remains unrepresentable; its payment-ticket line is marked financial, so the print is refused rather
than falling back to graphics.

View medium (1)
5. Browser print stays off after switching ✓ Resolved

Resolving commit: 0f8a899

Resolution reason
The Use Browser Print handler now updates the printer store and calls a new onPersistPrintMethod
callback (wired in settings/page.tsx to update saved printing state) before showing the success
toast. An e2e test asserts the browser method is persisted to localStorage and survives navigating
away and back.
Description
The Use Browser Print handler changes only printingForm but immediately shows a success toast
saying the print method was switched. Until the separate printing-settings save runs, printing still
uses the previously persisted method, and leaving the page discards the apparent one-click switch.

Grey Divider

Context sources
✅ Compliance rules (platform): 5 rules
✅ Cross-repo context — repo relationships
  Explored: repo: FreeOpenSourcePOS/docs.flopos.com (sha: abe7cff3) — View relationship
Review mode: Auto: 🧠 Deep: Many independent logic paths: profiles, detection, encoders, migration, API validation, transliteration.

Grey Divider

Tip of the day
💡 Did you know, you can hide the parts of a finding you never read, like the evidence or the agent prompt

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread shared/print/thermal-capabilities.ts Resolved
Comment thread shared/print/thermal-capabilities.ts Resolved
Comment thread main/printers/profiles.ts Resolved
Comment thread main/printers/profiles.ts Resolved
…1502: 1. **Core Test Suite (Shard 1/4) - `test:print-labels`**: - **Invariant**: `isThermalTextRepresentable`, `selectThermalCodePage`, and `thermalTextFallback` must evaluate native codepage representability (plus single-diacritic `LATIN_ASCII_MAP` fallback when no native codepage encodes the string), while extended Latin fallback folding (`LATIN_EXTENDED_ASCII_MAP` and Unicode NFD `\p{Diacritic}` stripping on `\p{Script=Latin}\p{Mark}*`) must only run inside `normalizeThermalText` when raster fallback does not intercept the text and no native codepage can encode it. - **Fix**: Kept `transliterateThermalText` in `shared/print/thermal-capabilities.ts` scoped to native codepage checks and `LATIN_ASCII_MAP`, updated `thermalTextFallback` to check `transliterateThermalText` so explicit English/ASCII financial fallbacks (e.g. `'Tổng cộng'` -> `'TOTAL'`) and raster triggers on thermal profiles are preserved, and applied extended Latin / NFD diacritic folding in `normalizeThermalText`. Updated the post-normalization `isThermalTextRepresentable` assertions in `tests/impact-printer-support.test.ts`. 2. **Core Test Suite (Shard 4/4) - `test:backup`, Playwright E2E (Shard 1/4), End-to-End Playwright Tests, and Required Checks Gate**: - **Invariant**: Every test fixture and assertion expecting the current latest database schema version must match `getCurrentSchemaVersion()` (`125` after migration `125` `add_printer_profile_id`). - **Fix**: Updated `tests/backup-restore-production.test.ts` (expected migrated schema version `124` -> `125`) and `frontend/e2e/database-export-import.spec.ts` (`contaminatedPayload` `schema_version` `'124'` -> `'125'` so `/api/db/import` does not flag a version mismatch requiring a Master PIN). Verified locally with `npm run test:print-labels`, `npm run test:impact-printer`, `npm run test:thermal-capabilities`, `npm run test:receipt-column-oracle`, `npm run test:backup`, `npm run test:release-regressions`, `npm run test:upgrade-path`, `npm run test:schema-health`, `npm run test:migration-registry`, `npm run lint`, `npm run build`, `npm run build:frontend`, and Playwright E2E (`e2e/database-export-import.spec.ts` and `e2e/qa-settings-receipts-printers.spec.ts`)
@khaira777 khaira777 changed the title feat: add 76mm impact printer support and capability-based printer onboarding feat(print): add 76mm impact printer support Nov 7, 2026
@khaira777 khaira777 changed the title feat(print): add 76mm impact printer support feat: add 76mm impact printer support and printer profile onboarding Nov 7, 2026
@khaira777 khaira777 changed the title feat: add 76mm impact printer support and printer profile onboarding feat(print): add 76mm impact printer support Nov 7, 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.

feat(print): add native 76mm impact printer support and capability-based printer onboarding

1 participant