Chapter 14: POS Client Application

The Point of Sale Terminal

The POS Client is the primary interface for retail associates. It must be fast, reliable, and work offline when network connectivity is lost. This chapter provides complete specifications for building a production-grade POS terminal.


14.1 Technology Stack

ComponentTechnologyRationale
Framework.NET MAUI Blazor HybridCross-platform, native performance
Local DatabaseSQLiteEmbedded, zero-config, reliable
State ManagementFluxor or custom MVVMPredictable state changes
Hardware APIPlatform Invoke (P/Invoke)Direct hardware access
Sync EngineCustom HTTP + SignalRReal-time + batch sync

14.2 Architecture Overview

┌─────────────────────────────────────────────────────────────────────┐
│                        POS CLIENT APPLICATION                        │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐ │
│  │   Views     │  │  ViewModels │  │  Services   │  │  Hardware   │ │
│  │  (XAML/     │◄─┤  (State +   │◄─┤  (Business  │◄─┤  Drivers    │ │
│  │   Blazor)   │  │   Commands) │  │   Logic)    │  │             │ │
│  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘ │
│         │                │                │                │        │
│         └────────────────┴────────────────┴────────────────┘        │
│                                   │                                  │
│                          ┌────────▼────────┐                        │
│                          │  Local SQLite   │                        │
│                          │    Database     │                        │
│                          └────────┬────────┘                        │
│                                   │                                  │
│                          ┌────────▼────────┐                        │
│                          │   Sync Engine   │                        │
│                          │  (Online/Queue) │                        │
│                          └────────┬────────┘                        │
└──────────────────────────────────┬──────────────────────────────────┘
                                   │
                          ┌────────▼────────┐
                          │  Central API    │
                          │  (When Online)  │
                          └─────────────────┘

14.3 Screen Specifications

Screen 1: Login Screen

Purpose: Authenticate retail associates with fast PIN entry.

Route: /login

╔════════════════════════════════════════════════════════════════════╗
║                                                                    ║
║                    ┌──────────────────────────┐                    ║
║                    │                          │                    ║
║                    │       STORE LOGO         │                    ║
║                    │       [128x128]          │                    ║
║                    │                          │                    ║
║                    └──────────────────────────┘                    ║
║                                                                    ║
║                         NEXUS CLOTHING                             ║
║                      Greenbrier Mall (GM)                          ║
║                                                                    ║
║                    ┌──────────────────────────┐                    ║
║                    │                          │                    ║
║                    │  Enter Employee PIN      │                    ║
║                    │                          │                    ║
║                    │     ● ● ● ○ ○ ○          │                    ║
║                    │                          │                    ║
║                    └──────────────────────────┘                    ║
║                                                                    ║
║                    ┌─────┬─────┬─────┐                             ║
║                    │  1  │  2  │  3  │                             ║
║                    ├─────┼─────┼─────┤                             ║
║                    │  4  │  5  │  6  │                             ║
║                    ├─────┼─────┼─────┤                             ║
║                    │  7  │  8  │  9  │                             ║
║                    ├─────┼─────┼─────┤                             ║
║                    │ CLR │  0  │ ENT │                             ║
║                    └─────┴─────┴─────┘                             ║
║                                                                    ║
║                    [Manager Override]                               ║
║                                                                    ║
║  ─────────────────────────────────────────────────────────────     ║
║  Status: ● Online  |  Last Sync: 2 min ago  |  v1.2.0              ║
╚════════════════════════════════════════════════════════════════════╝

Components:

ComponentSpecification
Logo128x128px, tenant-specific
Store Name24px, Bold, Primary color
Location14px, Secondary text
PIN Display6 circles, filled = entered
Numpad80x80px buttons, touch-friendly
Clear (CLR)Resets PIN entry
Enter (ENT)Submits PIN for validation
Manager OverrideOpens manager auth dialog
Status BarConnection, sync, version

Behavior:

  • PIN validated locally first (hash comparison)
  • Failed attempts: 3 max before lockout
  • Lockout duration: 5 minutes (configurable)
  • Auto-login timeout: 30 seconds of inactivity returns to login

Screen 2: Main Sale Screen

Purpose: Primary transaction interface for ringing up sales.

Route: /sale

╔════════════════════════════════════════════════════════════════════╗
║ NEXUS CLOTHING - GM         Sarah M.          12/29/2024  2:45 PM  ║
╠════════════════════════════════════════════════════════════════════╣
║                                                                    ║
║  ┌─────────────────────────────────────────────────────────────┐  ║
║  │ [Scan Item or Enter SKU...]                        [SEARCH] │  ║
║  └─────────────────────────────────────────────────────────────┘  ║
║                                                                    ║
║  ┌──────────────────────────────────┐  ┌───────────────────────┐  ║
║  │ CART                         (3) │  │ TOTALS                │  ║
║  ├──────────────────────────────────┤  ├───────────────────────┤  ║
║  │                                  │  │                       │  ║
║  │ 1. Galaxy V-Neck Tee        $29  │  │ Subtotal:    $104.00  │  ║
║  │    Size: M | Color: Navy         │  │                       │  ║
║  │    Qty: 2         [-] [+]   $58  │  │ Discount:     -$10.00 │  ║
║  │                           [DEL]  │  │                       │  ║
║  │ ─────────────────────────────────│  │ Tax (6%):      $5.64  │  ║
║  │ 2. Slim Fit Chinos          $46  │  │                       │  ║
║  │    Size: 32 | Color: Khaki       │  │ ─────────────────────  │  ║
║  │    Qty: 1         [-] [+]   $46  │  │                       │  ║
║  │                           [DEL]  │  │ TOTAL:        $99.64  │  ║
║  │ ─────────────────────────────────│  │                       │  ║
║  │                                  │  │                       │  ║
║  │                                  │  └───────────────────────┘  ║
║  │                                  │                             ║
║  │                                  │  ┌───────────────────────┐  ║
║  │                                  │  │ [DISCOUNT]  [HOLD]    │  ║
║  │                                  │  │                       │  ║
║  │                                  │  │ [CUSTOMER]  [VOID]    │  ║
║  │                                  │  │                       │  ║
║  └──────────────────────────────────┘  │ ┌───────────────────┐ │  ║
║                                        │ │                   │ │  ║
║  ┌──────────────────────────────────┐  │ │      PAY          │ │  ║
║  │ Customer: John Smith             │  │ │     $99.64        │ │  ║
║  │ Loyalty: Gold (2,450 pts)        │  │ │                   │ │  ║
║  │ [Remove Customer]                │  │ └───────────────────┘ │  ║
║  └──────────────────────────────────┘  └───────────────────────┘  ║
║                                                                    ║
╠════════════════════════════════════════════════════════════════════╣
║ [F1 Help] [F2 Lookup] [F3 Returns] [F4 Reports]  ● Online  Rcpt#42 ║
╚════════════════════════════════════════════════════════════════════╝

Layout Regions:

RegionWidthContent
Header100%Store, associate, date/time
Search Bar100%SKU/barcode entry with search
Cart Panel60%Line items with quantity controls
Totals Panel40%Running totals, discounts, tax
Action Buttons40%Discount, Hold, Customer, Void
Pay Button40%Large, prominent payment trigger
Customer Info60%Attached customer details
Footer100%Function keys, status, receipt #

Cart Item Layout:

┌─────────────────────────────────────────────────────────────┐
│ 1. Galaxy V-Neck Tee                                   $29  │
│    Size: M | Color: Navy                                    │
│    Qty: 2                  [-] [+]                     $58  │
│                                                      [DEL]  │
└─────────────────────────────────────────────────────────────┘

Keyboard Shortcuts:

KeyAction
F1Help overlay
F2Product lookup
F3Returns mode
F4Quick reports
F5Price check
F8Suspend sale
F9Recall sale
F12Manager functions
EnterAdd scanned item
EscCancel current action

Screen 3: Customer Lookup

Purpose: Find or create customer records for loyalty tracking.

Route: /customer-lookup (Modal overlay)

╔════════════════════════════════════════════════════════════════════╗
║ CUSTOMER LOOKUP                                              [X]   ║
╠════════════════════════════════════════════════════════════════════╣
║                                                                    ║
║  ┌──────────────────────────────────────────────────────────────┐ ║
║  │ [Search by name, phone, email, or loyalty #...]              │ ║
║  └──────────────────────────────────────────────────────────────┘ ║
║                                                                    ║
║  ┌──────────────────────────────────────────────────────────────┐ ║
║  │ RESULTS (3 found)                                            │ ║
║  ├──────────────────────────────────────────────────────────────┤ ║
║  │                                                              │ ║
║  │  ○ John Smith                                                │ ║
║  │    Phone: (555) 123-4567                                     │ ║
║  │    Email: john.smith@email.com                               │ ║
║  │    Loyalty: Gold (2,450 pts)  |  Last Visit: 12/15/2024     │ ║
║  │                                                              │ ║
║  │  ───────────────────────────────────────────────────────────  │ ║
║  │                                                              │ ║
║  │  ○ Johnny Smith Jr.                                          │ ║
║  │    Phone: (555) 234-5678                                     │ ║
║  │    Email: johnny.jr@email.com                                │ ║
║  │    Loyalty: Silver (890 pts)  |  Last Visit: 11/20/2024     │ ║
║  │                                                              │ ║
║  │  ───────────────────────────────────────────────────────────  │ ║
║  │                                                              │ ║
║  │  ○ Jonathan Smithson                                         │ ║
║  │    Phone: (555) 345-6789                                     │ ║
║  │    Email: j.smithson@work.com                                │ ║
║  │    Loyalty: None  |  Last Visit: 10/05/2024                 │ ║
║  │                                                              │ ║
║  └──────────────────────────────────────────────────────────────┘ ║
║                                                                    ║
║  ┌────────────────────────────────────────────────────────────────╢
║  │                                                                ║
║  │  [NEW CUSTOMER]                      [SELECT]   [CANCEL]       ║
║  │                                                                ║
║  └────────────────────────────────────────────────────────────────╢
╚════════════════════════════════════════════════════════════════════╝

New Customer Form:

╔════════════════════════════════════════════════════════════════════╗
║ NEW CUSTOMER                                                 [X]   ║
╠════════════════════════════════════════════════════════════════════╣
║                                                                    ║
║  First Name *         Last Name *                                  ║
║  ┌──────────────────┐ ┌──────────────────────────────────────────┐ ║
║  │ John             │ │ Smith                                    │ ║
║  └──────────────────┘ └──────────────────────────────────────────┘ ║
║                                                                    ║
║  Phone *                        Email                              ║
║  ┌──────────────────────────┐   ┌────────────────────────────────┐ ║
║  │ (555) 123-4567           │   │ john.smith@email.com           │ ║
║  └──────────────────────────┘   └────────────────────────────────┘ ║
║                                                                    ║
║  Address Line 1                                                    ║
║  ┌──────────────────────────────────────────────────────────────┐ ║
║  │ 123 Main Street                                              │ ║
║  └──────────────────────────────────────────────────────────────┘ ║
║                                                                    ║
║  City                    State        ZIP                          ║
║  ┌────────────────────┐  ┌─────────┐  ┌───────────────────────────┐║
║  │ Virginia Beach     │  │ VA    ▼ │  │ 23451                     │║
║  └────────────────────┘  └─────────┘  └───────────────────────────┘║
║                                                                    ║
║  [ ] Enroll in Loyalty Program                                     ║
║  [ ] Subscribe to email marketing                                  ║
║                                                                    ║
║  ┌────────────────────────────────────────────────────────────────╢
║  │                                                                ║
║  │                                        [SAVE]   [CANCEL]       ║
║  │                                                                ║
║  └────────────────────────────────────────────────────────────────╢
╚════════════════════════════════════════════════════════════════════╝

Screen 4: Returns Processing

Purpose: Process merchandise returns and exchanges.

Route: /returns

╔════════════════════════════════════════════════════════════════════╗
║ RETURNS PROCESSING                                    [Exit Return]║
╠════════════════════════════════════════════════════════════════════╣
║                                                                    ║
║  STEP 1: FIND ORIGINAL TRANSACTION                                 ║
║  ┌──────────────────────────────────────────────────────────────┐ ║
║  │ Receipt #: [________________]  OR  [Lookup by Customer]      │ ║
║  └──────────────────────────────────────────────────────────────┘ ║
║                                                                    ║
║  ┌──────────────────────────────────────────────────────────────┐ ║
║  │ ORIGINAL TRANSACTION #12345                  12/20/2024      │ ║
║  ├──────────────────────────────────────────────────────────────┤ ║
║  │ Customer: John Smith                                         │ ║
║  │ Payment: Visa ****4242                                       │ ║
║  ├──────────────────────────────────────────────────────────────┤ ║
║  │                                                              │ ║
║  │  [x] 1. Galaxy V-Neck Tee (M, Navy)             $29.00       │ ║
║  │      Reason: [Wrong Size           ▼]                        │ ║
║  │      Condition: [Good - Resellable ▼]                        │ ║
║  │                                                              │ ║
║  │  [ ] 2. Slim Fit Chinos (32, Khaki)             $46.00       │ ║
║  │                                                              │ ║
║  │  [ ] 3. Leather Belt (M)                        $35.00       │ ║
║  │                                                              │ ║
║  └──────────────────────────────────────────────────────────────┘ ║
║                                                                    ║
║  ┌────────────────────────┐  ┌───────────────────────────────────┐║
║  │ RETURN SUMMARY         │  │ REFUND TO                         │║
║  ├────────────────────────┤  ├───────────────────────────────────┤║
║  │ Items: 1               │  │ ○ Original Payment (Visa ****42)  │║
║  │ Subtotal: $29.00       │  │ ○ Store Credit                    │║
║  │ Tax Refund: $1.74      │  │ ○ Cash                            │║
║  │ ──────────────────     │  │ ○ Exchange (Add to New Sale)      │║
║  │ TOTAL: $30.74          │  │                                   │║
║  └────────────────────────┘  └───────────────────────────────────┘║
║                                                                    ║
║  Manager Approval Required: [ ] Over $100  [ ] No Receipt         ║
║                                                                    ║
║  ┌────────────────────────────────────────────────────────────────╢
║  │                                                                ║
║  │  [SCAN RETURN ITEMS]              [PROCESS RETURN]  [CANCEL]   ║
║  │                                                                ║
║  └────────────────────────────────────────────────────────────────╢
╚════════════════════════════════════════════════════════════════════╝

Return Reasons (Configurable):

  • Wrong Size
  • Wrong Color
  • Defective
  • Changed Mind
  • Gift Return
  • Price Adjustment
  • Other

Return Conditions:

  • Good - Resellable
  • Damaged - Cannot Resell
  • Missing Tags - Markdown

Screen 5: Inventory Lookup

Purpose: Check stock levels across all locations.

Route: /inventory (Modal overlay)

╔════════════════════════════════════════════════════════════════════╗
║ INVENTORY LOOKUP                                             [X]   ║
╠════════════════════════════════════════════════════════════════════╣
║                                                                    ║
║  ┌──────────────────────────────────────────────────────────────┐ ║
║  │ [Search by SKU, name, or scan barcode...]            [SEARCH]│ ║
║  └──────────────────────────────────────────────────────────────┘ ║
║                                                                    ║
║  ┌──────────────────────────────────────────────────────────────┐ ║
║  │                                                              │ ║
║  │  Galaxy V-Neck Tee                                   $29.00  │ ║
║  │  SKU: NXJ1078-NAV-M                                          │ ║
║  │  ────────────────────────────────────────────────────────────│ ║
║  │                                                              │ ║
║  │  VARIANTS:                                                   │ ║
║  │  ┌────────────┬─────┬─────┬─────┬─────┬─────┬───────┐       │ ║
║  │  │ Size/Color │  HQ │  GM │  HM │  LM │  NM │ TOTAL │       │ ║
║  │  ├────────────┼─────┼─────┼─────┼─────┼─────┼───────┤       │ ║
║  │  │ S / Navy   │  12 │   3 │   2 │   4 │   1 │    22 │       │ ║
║  │  │ M / Navy   │  15 │   5*│   3 │   2 │   0 │    25 │       │ ║
║  │  │ L / Navy   │   8 │   4 │   1 │   3 │   2 │    18 │       │ ║
║  │  │ XL / Navy  │   4 │   2 │   0 │   1 │   1 │     8 │       │ ║
║  │  │ S / Black  │  10 │   2 │   3 │   2 │   2 │    19 │       │ ║
║  │  │ M / Black  │  18 │   6 │   4 │   5 │   3 │    36 │       │ ║
║  │  └────────────┴─────┴─────┴─────┴─────┴─────┴───────┘       │ ║
║  │                                                              │ ║
║  │  * Current Location (GM)                                     │ ║
║  │                                                              │ ║
║  │  Last Updated: 12/29/2024 2:30 PM                           │ ║
║  │                                                              │ ║
║  └──────────────────────────────────────────────────────────────┘ ║
║                                                                    ║
║  ┌────────────────────────────────────────────────────────────────╢
║  │                                                                ║
║  │  [REQUEST TRANSFER]    [PRICE CHECK]           [CLOSE]         ║
║  │                                                                ║
║  └────────────────────────────────────────────────────────────────╢
╚════════════════════════════════════════════════════════════════════╝

Screen 6: End of Day

Purpose: Close register, balance cash, generate reports.

Route: /end-of-day

╔════════════════════════════════════════════════════════════════════╗
║ END OF DAY - Close Register                              [Cancel]  ║
╠════════════════════════════════════════════════════════════════════╣
║                                                                    ║
║  Register: REGISTER-01 (GM)              Date: 12/29/2024          ║
║  Cashier: Sarah Miller                   Shift: 9:00 AM - 5:30 PM  ║
║                                                                    ║
║  ┌──────────────────────────────────────────────────────────────┐ ║
║  │ SALES SUMMARY                                                │ ║
║  ├──────────────────────────────────────────────────────────────┤ ║
║  │                                                              │ ║
║  │  Total Transactions:            47                           │ ║
║  │  Gross Sales:               $3,245.67                        │ ║
║  │  Returns:                     -$125.00                       │ ║
║  │  Discounts:                   -$89.50                        │ ║
║  │  ──────────────────────────────────────                      │ ║
║  │  Net Sales:                 $3,031.17                        │ ║
║  │  Tax Collected:               $181.87                        │ ║
║  │                                                              │ ║
║  └──────────────────────────────────────────────────────────────┘ ║
║                                                                    ║
║  ┌──────────────────────────────────────────────────────────────┐ ║
║  │ CASH COUNT                                                   │ ║
║  ├──────────────────────────────────────────────────────────────┤ ║
║  │                                                              │ ║
║  │  Starting Cash:      $200.00                                 │ ║
║  │  Cash Sales:         $845.50                                 │ ║
║  │  Cash Returns:       -$45.00                                 │ ║
║  │  ──────────────────────────────────────                      │ ║
║  │  Expected Cash:    $1,000.50                                 │ ║
║  │                                                              │ ║
║  │  Counted Cash:     [_______________]  <-- Enter amount       │ ║
║  │                                                              │ ║
║  │  Variance:         $___.__ (Calculates automatically)        │ ║
║  │                                                              │ ║
║  └──────────────────────────────────────────────────────────────┘ ║
║                                                                    ║
║  ┌──────────────────────────────────────────────────────────────┐ ║
║  │ PAYMENT BREAKDOWN                                            │ ║
║  ├──────────────────────────────────────────────────────────────┤ ║
║  │  Cash:                  $845.50   (28 trans)                 │ ║
║  │  Credit Card:         $1,856.32   (15 trans)                 │ ║
║  │  Debit Card:            $254.35   (3 trans)                  │ ║
║  │  Store Credit:           $75.00   (1 trans)                  │ ║
║  └──────────────────────────────────────────────────────────────┘ ║
║                                                                    ║
║  ┌────────────────────────────────────────────────────────────────╢
║  │                                                                ║
║  │  [PRINT REPORT]  [RECOUNT]           [CLOSE REGISTER]          ║
║  │                                                                ║
║  └────────────────────────────────────────────────────────────────╢
╚════════════════════════════════════════════════════════════════════╝

14.4 Payment Screen

Purpose: Process various payment methods.

╔════════════════════════════════════════════════════════════════════╗
║ PAYMENT                                                      [X]   ║
╠════════════════════════════════════════════════════════════════════╣
║                                                                    ║
║                    AMOUNT DUE: $99.64                              ║
║                                                                    ║
║  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐    ║
║  │                 │  │                 │  │                 │    ║
║  │   [CREDIT]      │  │   [DEBIT]       │  │   [CASH]        │    ║
║  │      CARD       │  │     CARD        │  │                 │    ║
║  │                 │  │                 │  │                 │    ║
║  └─────────────────┘  └─────────────────┘  └─────────────────┘    ║
║                                                                    ║
║  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐    ║
║  │                 │  │                 │  │                 │    ║
║  │   [GIFT]        │  │   [STORE]       │  │   [SPLIT]       │    ║
║  │    CARD         │  │   CREDIT        │  │   PAYMENT       │    ║
║  │                 │  │                 │  │                 │    ║
║  └─────────────────┘  └─────────────────┘  └─────────────────┘    ║
║                                                                    ║
║  ═══════════════════════════════════════════════════════════════  ║
║                                                                    ║
║  CASH QUICK AMOUNTS:                                               ║
║                                                                    ║
║  ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐     ║
║  │  $20  │ │  $50  │ │ $100  │ │ $120  │ │ EXACT │ │ OTHER │     ║
║  └───────┘ └───────┘ └───────┘ └───────┘ └───────┘ └───────┘     ║
║                                                                    ║
║                    Amount Tendered: $________                      ║
║                    Change Due:      $________                      ║
║                                                                    ║
║  ┌────────────────────────────────────────────────────────────────╢
║  │                                                                ║
║  │                                        [PROCESS]   [CANCEL]    ║
║  │                                                                ║
║  └────────────────────────────────────────────────────────────────╢
╚════════════════════════════════════════════════════════════════════╝

14.5 State Management

Application State Model

public class PosState
{
    // Authentication
    public AuthState Auth { get; set; }

    // Current Transaction
    public TransactionState Transaction { get; set; }

    // Cart Items
    public List<CartItem> Cart { get; set; }

    // Customer
    public CustomerState Customer { get; set; }

    // Register
    public RegisterState Register { get; set; }

    // Sync Status
    public SyncState Sync { get; set; }

    // UI State
    public UiState Ui { get; set; }
}

public class TransactionState
{
    public string TransactionId { get; set; }
    public TransactionType Type { get; set; }  // Sale, Return, Exchange
    public TransactionStatus Status { get; set; }
    public decimal Subtotal { get; set; }
    public decimal DiscountTotal { get; set; }
    public decimal TaxTotal { get; set; }
    public decimal GrandTotal { get; set; }
    public List<PaymentEntry> Payments { get; set; }
    public decimal BalanceDue { get; set; }
}

State Actions

ActionDescription
AddToCartAdd item with quantity
UpdateQuantityChange line item quantity
RemoveFromCartDelete line item
ApplyDiscountAdd transaction/line discount
AttachCustomerLink customer to sale
ProcessPaymentRecord payment entry
VoidTransactionCancel entire transaction
SuspendSalePark sale for later
RecallSaleResume suspended sale

14.6 Sync Service Design

Sync Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                        SYNC ENGINE                                   │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  ┌─────────────┐     ┌─────────────┐     ┌─────────────┐            │
│  │   OUTBOUND  │     │   INBOUND   │     │   CONFLICT  │            │
│  │    QUEUE    │────▶│   HANDLER   │────▶│   RESOLVER  │            │
│  │ (SQLite)    │     │  (API Sync) │     │             │            │
│  └─────────────┘     └─────────────┘     └─────────────┘            │
│         │                   │                   │                    │
│         ▼                   ▼                   ▼                    │
│  ┌─────────────────────────────────────────────────────┐            │
│  │              LOCAL SQLITE DATABASE                   │            │
│  │  - Transactions (pending sync)                       │            │
│  │  - Products (cached catalog)                         │            │
│  │  - Customers (cached records)                        │            │
│  │  - Inventory (last known levels)                     │            │
│  │  - Sync metadata (timestamps, versions)              │            │
│  └─────────────────────────────────────────────────────┘            │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

Sync Interval

The sync engine runs on a 30-second polling cycle with immediate sync on reconnect:

TriggerBehavior
Timer (every 30s)Poll for outbound queue items and inbound updates
ReconnectImmediately flush all pending outbound items when connectivity restored
ManualUser can trigger via status bar “Sync Now” action
Transaction completeImmediate outbound sync attempt for completed transactions

Sync Priorities

PriorityData TypeFrequencyDirection
1 (Critical)TransactionsImmediateOutbound
2 (High)Inventory Changes5 minBoth
3 (Medium)Customers15 minBoth
4 (Low)Products1 hourInbound
5 (Batch)ReportsDailyOutbound

Conflict Resolution: When offline transactions sync, inventory conflicts (e.g., stock sold by another terminal) are resolved using the strategies defined in Chapter 05 Section 5.6 (Offline-First Architecture). The POS Client applies partial-fulfillment or last-write-wins depending on the entity type. See also Chapter 04 Section L.10A.1 for CRDT-based conflict resolution patterns.

Offline Queue Schema

CREATE TABLE sync_queue (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    entity_type TEXT NOT NULL,       -- 'transaction', 'customer', etc.
    entity_id TEXT NOT NULL,
    action TEXT NOT NULL,            -- 'create', 'update', 'delete'
    payload TEXT NOT NULL,           -- JSON serialized data
    priority INTEGER DEFAULT 5,
    retry_count INTEGER DEFAULT 0,
    created_at TEXT NOT NULL,
    last_attempt TEXT,
    status TEXT DEFAULT 'pending'    -- 'pending', 'syncing', 'failed', 'synced'
);

CREATE INDEX idx_sync_queue_status ON sync_queue(status, priority);

Offline Queue Limits

ThresholdBehavior
< 80 queued transactionsNormal operation
80 queued transactionsYellow warning banner: “80 transactions pending sync – connect to network soon”
100 queued transactionsBlock new sales. Red banner: “Maximum offline transactions reached. Connect to network to sync before processing new sales.”

Rationale: The 100-transaction limit prevents unbounded local data growth and reduces conflict risk during bulk sync. The warning at 80 gives associates time to find connectivity before hitting the hard limit.

Parked Sales (Hold/Recall)

Associates can suspend (“park”) an in-progress transaction to serve the next customer, then recall it later.

ConstraintValue
Maximum parked sales5 per register
Time-to-live (TTL)4 hours from park time
Expiry behaviorAuto-void after TTL, items returned to available inventory
RecallAny associate on the same register can recall

Parked Sale States:

Active Sale → [HOLD] → Parked (timer starts)
                              │
                    ├── [RECALL] → Resume as Active Sale
                    └── [4h TTL expires] → Auto-Void → Inventory restored

14.7 Hardware Integration

Receipt Printer

public interface IReceiptPrinter
{
    Task<bool> PrintReceiptAsync(Receipt receipt);
    Task<bool> OpenCashDrawerAsync();
    Task<bool> CutPaperAsync();
    Task<PrinterStatus> GetStatusAsync();
}

public class EpsonTM88Printer : IReceiptPrinter
{
    private readonly string _portName;

    public async Task<bool> PrintReceiptAsync(Receipt receipt)
    {
        var commands = new List<byte>();

        // Initialize printer
        commands.AddRange(new byte[] { 0x1B, 0x40 });  // ESC @

        // Center align
        commands.AddRange(new byte[] { 0x1B, 0x61, 0x01 });  // ESC a 1

        // Store header (double width/height)
        commands.AddRange(new byte[] { 0x1D, 0x21, 0x11 });  // GS ! 0x11
        commands.AddRange(Encoding.ASCII.GetBytes(receipt.StoreName + "\n"));

        // Reset text size
        commands.AddRange(new byte[] { 0x1D, 0x21, 0x00 });

        // ... additional formatting

        // Cut paper
        commands.AddRange(new byte[] { 0x1D, 0x56, 0x00 });  // GS V 0

        return await SendToPortAsync(commands.ToArray());
    }
}

Barcode Scanner

public interface IBarcodeScanner
{
    event EventHandler<BarcodeScannedEventArgs> BarcodeScanned;
    Task StartListeningAsync();
    Task StopListeningAsync();
}

public class HoneywellScanner : IBarcodeScanner
{
    public event EventHandler<BarcodeScannedEventArgs> BarcodeScanned;

    private SerialPort _port;

    public async Task StartListeningAsync()
    {
        _port = new SerialPort("COM3", 9600);
        _port.DataReceived += OnDataReceived;
        _port.Open();
    }

    private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        var barcode = _port.ReadLine().Trim();
        BarcodeScanned?.Invoke(this, new BarcodeScannedEventArgs(barcode));
    }
}

Cash Drawer

public interface ICashDrawer
{
    Task<bool> OpenAsync();
    Task<bool> IsOpenAsync();
}

public class ApgCashDrawer : ICashDrawer
{
    private readonly IReceiptPrinter _printer;

    public async Task<bool> OpenAsync()
    {
        // Most cash drawers open via printer kick command
        return await _printer.OpenCashDrawerAsync();
    }
}

Receipt Printing Workflow

Sale Completed
    │
    ├── Print receipt via ESC/POS to thermal printer
    │     ├── Success → Open cash drawer (if cash payment)
    │     └── Failure → Show "Printer Error" toast
    │                    ├── [Retry] → Resend ESC/POS commands
    │                    └── [Email Receipt] → Fallback to email
    │
    ├── Reprint from Transaction History
    │     └── Tap receipt icon on any past transaction → reprint
    │
    └── Email Receipt (optional)
          └── If customer has email on file → send digital receipt
TriggerAction
Sale completedAuto-print if printer connected
Cash paymentPrint receipt + open cash drawer
Card paymentPrint receipt (drawer stays closed)
Return completedPrint return receipt with refund details
Reprint requestFrom transaction history, tap receipt icon
Printer offlineOffer email receipt as fallback

14.8 Local Database Schema

The POS Client uses 6 core SQLite tables for offline-first operation. These tables are the local source of truth when offline, synced to the central PostgreSQL database when connectivity is restored.

-- ============================================================
-- TABLE 1: products (cached product catalog from central)
-- ============================================================
CREATE TABLE products (
    id TEXT PRIMARY KEY,
    sku TEXT NOT NULL UNIQUE,
    barcode TEXT,
    name TEXT NOT NULL,
    description TEXT,
    price REAL NOT NULL,
    cost REAL,
    category_id TEXT,
    tax_rate REAL DEFAULT 0,
    quantity_on_hand INTEGER DEFAULT 0,    -- cached stock at this location
    is_active INTEGER DEFAULT 1,
    last_synced TEXT NOT NULL
);

CREATE INDEX idx_products_barcode ON products(barcode);
CREATE INDEX idx_products_sku ON products(sku);

-- ============================================================
-- TABLE 2: customers (cached customer records)
-- ============================================================
CREATE TABLE customers (
    id TEXT PRIMARY KEY,
    first_name TEXT NOT NULL,
    last_name TEXT NOT NULL,
    phone TEXT,
    email TEXT,
    loyalty_tier TEXT,
    loyalty_points INTEGER DEFAULT 0,
    last_synced TEXT NOT NULL
);

-- ============================================================
-- TABLE 3: transactions (local-first, queued for sync)
-- Includes line items and payments as JSON for atomic sync
-- ============================================================
CREATE TABLE transactions (
    id TEXT PRIMARY KEY,
    transaction_number INTEGER NOT NULL,
    type TEXT NOT NULL,                     -- 'sale', 'return', 'exchange'
    status TEXT NOT NULL,                   -- 'in_progress', 'completed', 'voided'
    customer_id TEXT,
    associate_id TEXT NOT NULL,
    register_id TEXT NOT NULL,
    subtotal REAL NOT NULL,
    discount_total REAL DEFAULT 0,
    tax_total REAL NOT NULL,
    grand_total REAL NOT NULL,
    line_items TEXT NOT NULL,               -- JSON array of line items
    payments TEXT,                          -- JSON array of payment entries
    created_at TEXT NOT NULL,
    completed_at TEXT,
    synced_at TEXT,
    FOREIGN KEY (customer_id) REFERENCES customers(id)
);

-- ============================================================
-- TABLE 4: sync_queue (pending outbound sync items)
-- Max 100 pending transactions before blocking new sales
-- ============================================================
CREATE TABLE sync_queue (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    entity_type TEXT NOT NULL,              -- 'transaction', 'customer', etc.
    entity_id TEXT NOT NULL,
    action TEXT NOT NULL,                   -- 'create', 'update', 'delete'
    payload TEXT NOT NULL,                  -- JSON serialized data
    priority INTEGER DEFAULT 5,
    retry_count INTEGER DEFAULT 0,
    max_retries INTEGER DEFAULT 10,
    created_at TEXT NOT NULL,
    last_attempt TEXT,
    status TEXT DEFAULT 'pending'           -- 'pending', 'syncing', 'failed', 'synced'
);

CREATE INDEX idx_sync_queue_status ON sync_queue(status, priority);

-- ============================================================
-- TABLE 5: parked_sales (suspended transactions)
-- Max 5 per register, 4-hour TTL
-- ============================================================
CREATE TABLE parked_sales (
    id TEXT PRIMARY KEY,
    register_id TEXT NOT NULL,
    associate_id TEXT NOT NULL,
    customer_id TEXT,
    cart_json TEXT NOT NULL,                -- JSON: full cart state (items, discounts, customer)
    subtotal REAL NOT NULL,
    note TEXT,                              -- optional note ("Customer getting wallet")
    parked_at TEXT NOT NULL,
    expires_at TEXT NOT NULL,               -- parked_at + 4 hours
    recalled_at TEXT,
    status TEXT DEFAULT 'parked'            -- 'parked', 'recalled', 'expired'
);

CREATE INDEX idx_parked_sales_register ON parked_sales(register_id, status);

-- ============================================================
-- TABLE 6: config (local settings and sync metadata)
-- ============================================================
CREATE TABLE config (
    key TEXT PRIMARY KEY,
    value TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

-- Default config entries:
-- 'register_id'          → UUID of this register
-- 'location_code'        → Store code (e.g., 'GM')
-- 'tenant_id'            → Tenant identifier
-- 'last_full_sync'       → ISO timestamp of last complete sync
-- 'last_product_sync'    → ISO timestamp of last product catalog sync
-- 'sync_interval_ms'     → Sync polling interval (default: 30000)
-- 'offline_tx_limit'     → Max queued transactions (default: 100)
-- 'offline_tx_warning'   → Warning threshold (default: 80)
-- 'parked_sale_limit'    → Max parked sales (default: 5)
-- 'parked_sale_ttl_min'  → TTL in minutes (default: 240)

14.9 Performance Requirements

Performance Budget

OperationTargetPercentileMeasurement
Complete checkout< 500msp99Tap “Pay” to receipt printed (the critical path)
Barcode scan to item display< 200msp95SQLite lookup + cart render
Product search< 200msp95Keystroke to results
App cold start< 3 secondsp95Launch to login screen ready
Sync cycle< 5 secondsp95Background sync round-trip
Offline switchInstantp99Seamless transition, no UI jank
Receipt print< 1 secondp95ESC/POS command to paper cut
Parked sale recall< 300msp95Select to full cart restored

Checkout Budget (500ms p99): This is the most critical performance target. The 500ms budget covers: validate cart (50ms) + calculate tax (50ms) + record payment (100ms) + update inventory (50ms) + generate receipt (100ms) + print receipt (150ms). All operations hit local SQLite; server sync happens asynchronously after the customer interaction completes.


14.10 Security Considerations

ConcernMitigation
PIN StorageHashed with bcrypt, salted
Local DBSQLCipher encryption
API TokensSecure storage (Keychain/DPAPI)
PCI ComplianceNo card data stored locally
Session TimeoutAuto-logout after inactivity
Audit TrailAll actions logged with timestamp

14.11 Summary

The POS Client Application is designed for:

  1. Speed: Sub-second response times for all common operations
  2. Reliability: Full offline capability with automatic sync
  3. Usability: Touch-friendly, keyboard shortcuts, minimal training
  4. Security: PIN auth, encrypted storage, audit logging
  5. Integration: Hardware support for printers, scanners, drawers

Cross-Reference: For detailed offline conflict resolution logic, see Chapter 05 Section 1.16.3.

Next: Chapter 15: Tenant Admin Portal covers the Merchant Dashboard.


Document Information

AttributeValue
Version5.0.0
Created2025-12-29
Updated2026-02-25
AuthorClaude Code
StatusActive
PartV - Frontend
Chapter14 of 32

This chapter is part of the POS Blueprint Book. All content is self-contained.