Chapter 16: Mobile Raptag Application
RFID Inventory Management
The Raptag mobile application enables rapid inventory counting using RFID technology. Associates can scan entire racks of merchandise in seconds, dramatically reducing inventory count time and improving accuracy.
Configuration Note: RFID settings and device management are configured in the Tenant Admin Portal (Chapter 15), not in the mobile app itself. The mobile app downloads its configuration from the central API on startup. See Chapter 15: Settings > RFID for device registration, printer setup, and tag configuration.
16.1 Technology Stack
| Component | Technology | Rationale |
|---|---|---|
| Framework | .NET MAUI | Cross-platform, native RFID SDK access |
| RFID SDK | Zebra RFID SDK | Enterprise-grade, widely deployed |
| Local Database | SQLite | Offline-capable, lightweight |
| Sync | REST API + Background Service | Reliable batch uploads |
| Tag Printing | Zebra ZPL | Industry standard label format |
16.2 Architecture Overview
┌─────────────────────────────────────────────────────────────────────┐
│ RAPTAG MOBILE APPLICATION │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ UI LAYER ││
│ │ Login │ Dashboard │ Session │ Scanning │ Summary │ Sync ││
│ └─────────────────────────────────────────────────────────────────┘│
│ │ │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ VIEW MODELS ││
│ │ LoginVM │ SessionVM │ ScanVM │ SummaryVM │ SyncVM ││
│ └─────────────────────────────────────────────────────────────────┘│
│ │ │
│ ┌───────────────┬───────────────┬───────────────┬─────────────────┐│
│ │ RFID Service │ Sync Service │ Print Service │ Session Service ││
│ │ (Zebra SDK) │ (HTTP/Queue) │ (ZPL/BT) │ (State Mgmt) ││
│ └───────────────┴───────────────┴───────────────┴─────────────────┘│
│ │ │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ LOCAL SQLITE DATABASE ││
│ │ - Sessions - Tags ││
│ │ - Scan Records - Product Cache ││
│ │ - Sync Queue - Settings ││
│ └─────────────────────────────────────────────────────────────────┘│
│ │
└─────────────────────────────────────────────────────────────────────┘
│
┌────────▼────────┐
│ Central API │
│ (When Online) │
└─────────────────┘
16.3 Screen Specifications
Screen 1: Login
Purpose: Authenticate user and select operational context.
Route: /login
╔════════════════════════════════════════════════════════════════════╗
║ ║
║ ║
║ ┌──────────────────────────┐ ║
║ │ │ ║
║ │ ████████████████ │ ║
║ │ ██ RAPTAG ██ │ ║
║ │ ████████████████ │ ║
║ │ │ ║
║ └──────────────────────────┘ ║
║ ║
║ RFID Inventory System ║
║ ║
║ ║
║ ┌────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ Employee PIN │ ║
║ │ ┌────────────────────────────────────────────────────┐ │ ║
║ │ │ ● ● ● ● ○ ○ │ │ ║
║ │ └────────────────────────────────────────────────────┘ │ ║
║ │ │ ║
║ │ ┌───────┐ ┌───────┐ ┌───────┐ │ ║
║ │ │ 1 │ │ 2 │ │ 3 │ │ ║
║ │ └───────┘ └───────┘ └───────┘ │ ║
║ │ ┌───────┐ ┌───────┐ ┌───────┐ │ ║
║ │ │ 4 │ │ 5 │ │ 6 │ │ ║
║ │ └───────┘ └───────┘ └───────┘ │ ║
║ │ ┌───────┐ ┌───────┐ ┌───────┐ │ ║
║ │ │ 7 │ │ 8 │ │ 9 │ │ ║
║ │ └───────┘ └───────┘ └───────┘ │ ║
║ │ ┌───────┐ ┌───────┐ ┌───────┐ │ ║
║ │ │ CLR │ │ 0 │ │ GO │ │ ║
║ │ └───────┘ └───────┘ └───────┘ │ ║
║ │ │ ║
║ └────────────────────────────────────────────────────────────┘ ║
║ ║
║ ║
║ ──────────────────────────────────────────────────────────────── ║
║ Reader: MC3390R | Battery: 85% | ● Offline ║
╚════════════════════════════════════════════════════════════════════╝
Components:
| Component | Specification |
|---|---|
| Logo | Raptag brand, centered |
| PIN Display | 6 digits with masked/filled indicators |
| Numpad | Large touch targets (64x64px min) |
| Clear (CLR) | Reset PIN entry |
| Go | Submit PIN for validation |
| Status Bar | Reader model, battery, connection |
Behavior:
- PIN validated against local cache (for offline)
- Sync user list on startup when online
- Auto-login from last session option
- Lock screen after 5 minutes of inactivity
Screen 2: Home Dashboard
Purpose: At-a-glance overview of assigned counts, recent activity, sync health, and device status.
Route: /home
╔════════════════════════════════════════════════════════════════════╗
║ RAPTAG [⚙] [Logout] ║
╠════════════════════════════════════════════════════════════════════╣
║ ║
║ Welcome, Sarah Miller ║
║ 📍 GM - Greenbrier Mall Today: December 29, 2024 ║
║ ║
║ MY ASSIGNED COUNTS ║
║ ┌────────────────────────────────────────────────────────────────╢
║ │ ║
║ │ ▶ Full Inventory - Section A Due: Today 5:00 PM ║
║ │ Expected: 505 items Assigned by: Manager ║
║ │ ║
║ │ ▶ Cycle Count - Section C Due: Tomorrow 10:00 AM ║
║ │ Expected: 312 items Assigned by: Manager ║
║ │ ║
║ └────────────────────────────────────────────────────────────────╢
║ ║
║ RECENT SESSIONS ║
║ ┌────────────────────────────────────────────────────────────────╢
║ │ ✓ #GM-1228-003 Spot Check 156 items Synced ║
║ │ ✓ #GM-1228-001 Cycle Count 289 items Synced ║
║ │ ● #GM-1227-002 Full Inventory 2,847 items Pending Upload ║
║ └────────────────────────────────────────────────────────────────╢
║ ║
║ ┌─────────────────────────┐ ┌─────────────────────────────────╢
║ │ SYNC STATUS │ │ DEVICE HEALTH ║
║ │ ● 1 session pending │ │ Battery: 85% ● ║
║ │ Last sync: 5 min ago │ │ Reader: MC3390R Connected ║
║ │ │ │ Storage: 2.1 GB free ║
║ └─────────────────────────┘ └─────────────────────────────────╢
║ ║
║ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ ┌────────────┐║
║ │ NEW SESSION │ │ JOIN SESSION │ │ PRINT TAGS │ │ SYNC NOW │║
║ └──────────────┘ └──────────────┘ └────────────┘ └────────────┘║
║ ║
║ ──────────────────────────────────────────────────────────────── ║
║ Reader: MC3390R | ● Battery: 85% | ● Online ║
╚════════════════════════════════════════════════════════════════════╝
Dashboard Components:
| Component | Data Source | Refresh |
|---|---|---|
| Assigned Counts | GET /api/rfid/sessions?assigned_to={operator}&status=pending | On screen load |
| Recent Sessions | Local SQLite sessions table, last 5 | Real-time |
| Sync Status | Local sync_queue pending count | Real-time |
| Device Health | System APIs (battery, storage, Bluetooth) | Every 30s |
Quick Actions:
| Button | Action |
|---|---|
| New Session | Navigate to Screen 3 (Session Start) |
| Join Session | Navigate to Screen 3, scroll to “Join Existing Session” |
| Print Tags | Navigate to Tag Printing screen (18.5) |
| Sync Now | Trigger immediate sync of pending sessions |
Screen 3: Session Start
Purpose: Configure a new inventory counting session or join an existing one.
Route: /session/new
╔════════════════════════════════════════════════════════════════════╗
║ NEW INVENTORY SESSION [< Back] ║
╠════════════════════════════════════════════════════════════════════╣
║ ║
║ ────────────────────────────────────────────────────────────── ║
║ ║
║ LOCATION * ║
║ ┌────────────────────────────────────────────────────────────┐ ║
║ │ GM - Greenbrier Mall ▼ │ ║
║ └────────────────────────────────────────────────────────────┘ ║
║ ║
║ COUNT TYPE * ║
║ ┌────────────────────────────────────────────────────────────┐ ║
║ │ ○ Full Inventory │ ║
║ │ Complete inventory of entire location │ ║
║ │ │ ║
║ │ ● Cycle Count │ ║
║ │ Count specific section/department │ ║
║ │ │ ║
║ │ ○ Spot Check │ ║
║ │ Quick verification of selected items │ ║
║ │ │ ║
║ │ ○ Find Item │ ║
║ │ Locate specific product by EPC/SKU │ ║
║ └────────────────────────────────────────────────────────────┘ ║
║ ║
║ SECTION (Required for Cycle Count) ║
║ ┌────────────────────────────────────────────────────────────┐ ║
║ │ Section A - Men's Tops ▼ │ ║
║ └────────────────────────────────────────────────────────────┘ ║
║ ║
║ NOTES (Optional) ║
║ ┌────────────────────────────────────────────────────────────┐ ║
║ │ Pre-inventory count for Q4 audit │ ║
║ │ │ ║
║ └────────────────────────────────────────────────────────────┘ ║
║ ║
║ ┌────────────────────────────────────────────────────────────┐ ║
║ │ START NEW SESSION │ ║
║ └────────────────────────────────────────────────────────────┘ ║
║ ║
║ ──────── OR ──────── ║
║ ║
║ JOIN EXISTING SESSION ║
║ ┌────────────────────────────────────────────────────────────┐ ║
║ │ ● #GM-2024-1229-001 Full Inventory 3 operators │ ║
║ │ Started: 1:30 PM Sections: B, D available │ ║
║ │ │ ║
║ │ ○ #GM-2024-1229-002 Cycle Count 1 operator │ ║
║ │ Started: 2:15 PM Section: E (Accessories) │ ║
║ └────────────────────────────────────────────────────────────┘ ║
║ ║
║ ┌────────────────────────────────────────────────────────────┐ ║
║ │ JOIN SELECTED SESSION │ ║
║ └────────────────────────────────────────────────────────────┘ ║
║ ║
║ ──────────────────────────────────────────────────────────────── ║
║ Reader: Ready | ● Battery: 85% | ● Online ║
╚════════════════════════════════════════════════════════════════════╝
Count Types (RFID counting only — no receiving; see BRD Section 5.16.4):
| Type | Use Case | Expected Items |
|---|---|---|
| Full Inventory | Annual/semi-annual full count | 2,000-100,000+ |
| Cycle Count | Section/department audits | 200-1,000 |
| Spot Check | Discrepancy verification | 10-50 |
| Find Item | Locate specific product by EPC | 1 |
Note: “Receiving” is handled by the barcode Scanner in the POS Client (Ch 14), not RFID. See BRD Section 5.16.6 for the Scanner vs RFID distinction.
Sections (Configurable per location in Admin Portal):
- Section A - Men’s Tops
- Section B - Men’s Bottoms
- Section C - Women’s Tops
- Section D - Women’s Bottoms
- Section E - Accessories
- Backroom
- Display Window
Join Session Flow:
- App queries
GET /api/rfid/sessions?location={code}&status=activefor active sessions at operator’s location - Operator selects a session and picks an available section
- App calls
POST /api/rfid/sessions/{id}/joinwith operator details - Server adds row to
session_operatorstable - Scanning screen opens with session context pre-loaded
- Deduplication: If multiple operators scan the same EPC, server keeps highest RSSI reading (see Chapter 05 Section 4.6.8 for RSSI-based dedup rules)
Business Rules:
- Maximum 10 operators per session (see Chapter 05 Section 5.16.4)
- One active session per operator (must complete or leave current session before joining another)
- Section assignment is advisory (not enforced by reader hardware)
- Session creator is automatically the first operator
Screen 4: Scanning (Main Interface)
Purpose: The primary RFID scanning interface during an active session.
Route: /session/scan
╔════════════════════════════════════════════════════════════════════╗
║ SCANNING - Cycle Count [Pause] [End] ║
╠════════════════════════════════════════════════════════════════════╣
║ ║
║ Location: GM - Greenbrier Mall Section: A - Men's Tops ║
║ Started: 2:45 PM Duration: 00:12:34 ║
║ Operator: Sarah Miller Session: #GM-2024-1229-001 ║
║ ║
║ PROGRESS ║
║ ████████████████████░░░░░░░░░░ 62% 312 of 505 items ║
║ Est. remaining: ~8 min ║
║ ║
║ ┌────────────────────────────────────────────────────────────────╢
║ │ ║
║ │ LIVE SCAN ║
║ │ ║
║ │ ┌──────────────────────────────────────────┐ ║
║ │ │ │ ║
║ │ │ ████ SCANNING ████ │ ║
║ │ │ │ ║
║ │ │ Tags Read: 847 │ ║
║ │ │ Unique Items: 312 │ ║
║ │ │ Read Rate: 42/sec │ ║
║ │ │ │ ║
║ │ └──────────────────────────────────────────┘ ║
║ │ ║
║ │ [HOLD TRIGGER TO SCAN] ║
║ │ ║
║ └────────────────────────────────────────────────────────────────╢
║ ║
║ RECENT SCANS ║
║ ┌────────────────────────────────────────────────────────────────╢
║ │ ║
║ │ ✓ NXJ1078-NAV-M Galaxy V-Neck (M, Navy) x3 ║
║ │ ✓ NXJ1078-NAV-L Galaxy V-Neck (L, Navy) x2 ║
║ │ ✓ NXP0892-KHK-32 Slim Fit Chinos (32, Khaki) x1 ║
║ │ ! UNKNOWN TAG E280116060000... x1 ║
║ │ ✓ NXA0234-BLK-M Leather Belt (M, Black) x4 ║
║ │ ║
║ │ [View All 312 Items] ║
║ │ ║
║ └────────────────────────────────────────────────────────────────╢
║ ║
║ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ║
║ │ [MANUAL ADD] │ │ [FIND ITEM] │ │ [SETTINGS] │ ║
║ └─────────────────┘ └─────────────────┘ └─────────────────┘ ║
║ ║
║ ──────────────────────────────────────────────────────────────── ║
║ Reader: Scanning | ● Battery: 82% | Signal: Strong ║
║ Auto-save: 30s ago | Checkpoint: 847 reads ║
╚════════════════════════════════════════════════════════════════════╝
Progress Tracking:
| Metric | Source | Display |
|---|---|---|
| Progress % | unique_items / expected_count × 100 | Progress bar + percentage |
| Items count | "312 of 505 items" | Current unique vs expected |
| Time estimate | (elapsed / progress%) × remaining% | "~8 min remaining" |
| Auto-save | last_checkpoint_at vs now | "Auto-save: 30s ago" |
Note:
expected_countcomes from the server when the session is created (based on last known inventory at that location/section). If unavailable (offline session start), the progress bar is hidden and only raw counts are shown.
Scanning States:
IDLE STATE SCANNING STATE
┌────────────────────────┐ ┌────────────────────────┐
│ │ │ │
│ ○ ○ ○ ○ ○ ○ ○ ○ │ │ ████████████████ │
│ │ │ ████ ACTIVE ████ │
│ Ready to Scan │ │ ████████████████ │
│ │ │ │
│ Tags: 0 │ │ Tags: 847 │
│ │ │ Rate: 42/sec │
│ │ │ │
└────────────────────────┘ └────────────────────────┘
PAUSED STATE COMPLETED STATE
┌────────────────────────┐ ┌────────────────────────┐
│ │ │ │
│ ║ ║ PAUSED ║ ║ │ │ ✓ COMPLETE ✓ │
│ │ │ │
│ Session paused │ │ Session ended │
│ Tap to resume │ │ │
│ │ │ Total: 847 tags │
│ Tags: 312 │ │ 312 unique items │
│ │ │ │
└────────────────────────┘ └────────────────────────┘
Reader Signal Strength:
| Level | Icon | Read Rate |
|---|---|---|
| Strong | 4 bars | 40+ tags/sec |
| Good | 3 bars | 20-40 tags/sec |
| Fair | 2 bars | 10-20 tags/sec |
| Weak | 1 bar | < 10 tags/sec |
| None | X | No connection |
Quick Actions:
| Action | Purpose |
|---|---|
| Manual Add | Barcode scan for untagged items |
| Find Item | Locate specific SKU using reader |
| Settings | Adjust power, beep, vibration |
Screen 5: Session Summary
Purpose: Review results and submit completed count session.
Route: /session/summary
╔════════════════════════════════════════════════════════════════════╗
║ SESSION SUMMARY [< Back] ║
╠════════════════════════════════════════════════════════════════════╣
║ ║
║ ┌────────────────────────────────────────────────────────────────╢
║ │ SESSION #GM-2024-1229-001 ║
║ │ Cycle Count - Section A (Men's Tops) ║
║ │ Location: GM - Greenbrier Mall ║
║ │ Operator: Sarah Miller ║
║ │ Date: December 29, 2024 ║
║ │ Duration: 00:23:45 ║
║ └────────────────────────────────────────────────────────────────╢
║ ║
║ SCAN RESULTS ║
║ ┌─────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ ┌─────────────────────┐ ┌─────────────────────┐ │ ║
║ │ │ TOTAL TAGS │ │ UNIQUE ITEMS │ │ ║
║ │ │ 847 │ │ 312 │ │ ║
║ │ └─────────────────────┘ └─────────────────────┘ │ ║
║ │ │ ║
║ │ ┌─────────────────────┐ ┌─────────────────────┐ │ ║
║ │ │ EXPECTED │ │ VARIANCE │ │ ║
║ │ │ 305 │ │ +7 (2.3%) │ │ ║
║ │ └─────────────────────┘ └─────────────────────┘ │ ║
║ │ │ ║
║ └─────────────────────────────────────────────────────────────┘ ║
║ ║
║ DISCREPANCIES View All ║
║ ┌─────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ ▲ OVER (5 items) │ ║
║ │ NXJ1078-NAV-M Expected: 12 Counted: 15 (+3) │ ║
║ │ NXP0892-KHK-32 Expected: 8 Counted: 10 (+2) │ ║
║ │ │ ║
║ │ ▼ SHORT (3 items) │ ║
║ │ NXJ2156-WHT-L Expected: 6 Counted: 4 (-2) │ ║
║ │ NXA0234-BRN-M Expected: 5 Counted: 4 (-1) │ ║
║ │ │ ║
║ │ ? UNKNOWN (2 tags) │ ║
║ │ E280116060000207523456789 │ ║
║ │ E280116060000207523456790 │ ║
║ │ │ ║
║ └─────────────────────────────────────────────────────────────┘ ║
║ ║
║ ┌─────────────────────┐ ┌─────────────────────────────────────┐ ║
║ │ │ │ │ ║
║ │ [RECOUNT SECTION] │ │ SUBMIT SESSION │ ║
║ │ │ │ │ ║
║ └─────────────────────┘ └─────────────────────────────────────┘ ║
║ ║
║ ──────────────────────────────────────────────────────────────── ║
║ Reader: Idle | Battery: 78% | ● Online ║
╚════════════════════════════════════════════════════════════════════╝
Variance Thresholds (Configurable):
| Variance | Color | Action |
|---|---|---|
| 0% | Green | Auto-approve |
| 1-2% | Yellow | Review recommended |
| 3-5% | Orange | Manager review required |
| > 5% | Red | Recount required |
Screen 6: Sync Status
Purpose: Monitor data synchronization with central server.
Route: /sync
╔════════════════════════════════════════════════════════════════════╗
║ SYNC STATUS [< Menu] ║
╠════════════════════════════════════════════════════════════════════╣
║ ║
║ CONNECTION ║
║ ┌────────────────────────────────────────────────────────────────╢
║ │ ║
║ │ ● Connected to Central Server ║
║ │ Server: api.nexuspos.com ║
║ │ Latency: 45ms ║
║ │ Last Sync: 2 minutes ago ║
║ │ ║
║ └────────────────────────────────────────────────────────────────╢
║ ║
║ PENDING UPLOADS ║
║ ┌────────────────────────────────────────────────────────────────╢
║ │ ║
║ │ ┌─────────────────────────────────────────────────────────┐║
║ │ │ Session #GM-2024-1229-001 Uploading │║
║ │ │ 312 items, 847 tag reads │║
║ │ │ Progress: ████████████░░░░░░░░ 62% │║
║ │ └─────────────────────────────────────────────────────────┘║
║ │ ║
║ │ ┌─────────────────────────────────────────────────────────┐║
║ │ │ Session #GM-2024-1228-003 Pending │║
║ │ │ 156 items, 423 tag reads │║
║ │ │ Waiting... │║
║ │ └─────────────────────────────────────────────────────────┘║
║ │ ║
║ └────────────────────────────────────────────────────────────────╢
║ ║
║ RECENT SYNCS ║
║ ┌────────────────────────────────────────────────────────────────╢
║ │ ║
║ │ ✓ Product Catalog Updated 10 min ago 1,245 items ║
║ │ ✓ Tag Mappings Updated 10 min ago 8,432 tags ║
║ │ ✓ User List Updated 1 hour ago 24 users ║
║ │ ✓ Location Config Updated 1 hour ago 5 locations ║
║ │ ║
║ └────────────────────────────────────────────────────────────────╢
║ ║
║ ┌─────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ [SYNC NOW] │ ║
║ │ │ ║
║ └─────────────────────────────────────────────────────────────┘ ║
║ ║
║ Storage: 245 MB used of 2 GB | Last Full Sync: 12/29 9:00 AM ║
║ ║
║ ──────────────────────────────────────────────────────────────── ║
║ Reader: Idle | Battery: 78% | ● Online ║
╚════════════════════════════════════════════════════════════════════╝
16.4 Zebra RFID Reader Integration
Supported Devices
| Model | Form Factor | Range | Use Case |
|---|---|---|---|
| MC3390R | Handheld | 20 ft | Store counts |
| RFD40 | Sled | 12 ft | Attaches to phone |
| FX9600 | Fixed | 30 ft | Dock door receiving |
SDK Integration
public interface IRfidService
{
event EventHandler<TagReadEventArgs> TagRead;
event EventHandler<BatteryEventArgs> BatteryChanged;
event EventHandler<ReaderEventArgs> ReaderConnected;
event EventHandler<ReaderEventArgs> ReaderDisconnected;
Task<bool> ConnectAsync();
Task DisconnectAsync();
Task StartInventoryAsync();
Task StopInventoryAsync();
Task<ReaderStatus> GetStatusAsync();
Task SetPowerLevelAsync(int dbm);
}
public class ZebraRfidService : IRfidService
{
private readonly RFIDReader _reader;
private readonly EventHandler _eventHandler;
public async Task<bool> ConnectAsync()
{
var readers = RFIDReader.GetAvailableReaders();
if (readers.Count == 0) return false;
_reader = readers[0];
_reader.Events.TagReadEvent += OnTagRead;
_reader.Events.ReaderAppearEvent += OnReaderAppear;
_reader.Events.ReaderDisappearEvent += OnReaderDisappear;
_reader.Events.BatteryEvent += OnBatteryChanged;
return await _reader.ConnectAsync();
}
public async Task StartInventoryAsync()
{
var config = new InventoryConfig
{
MemoryBank = MEMORY_BANK.MEMORY_BANK_EPC,
ReportUnique = true,
StopTrigger = new StopTrigger
{
StopTriggerType = STOP_TRIGGER_TYPE.STOP_TRIGGER_TYPE_TAG_OBSERVATION
}
};
await _reader.Inventory.PerformAsync(config);
}
private void OnTagRead(object sender, TagDataEventArgs e)
{
foreach (var tag in e.ReadEventData.TagData)
{
var epc = tag.TagID;
var rssi = tag.PeakRSSI;
TagRead?.Invoke(this, new TagReadEventArgs(epc, rssi));
}
}
}
Power Level Settings
| Power (dBm) | Range | Battery Impact | Use Case |
|---|---|---|---|
| 30 (Max) | 20+ ft | High | Full store |
| 25 | 15 ft | Medium | Zone count |
| 20 | 10 ft | Low | Spot check |
| 15 | 5 ft | Minimal | Single rack |
16.5 Tag Printing Workflow
Encoding New Tags
╔════════════════════════════════════════════════════════════════════╗
║ PRINT RFID TAGS [< Back] ║
╠════════════════════════════════════════════════════════════════════╣
║ ║
║ PRODUCT ║
║ ┌────────────────────────────────────────────────────────────────╢
║ │ NXJ1078-NAV-M ║
║ │ Galaxy V-Neck Tee - Navy, Medium ║
║ │ Price: $29.00 ║
║ │ Current Stock: 15 (GM) ║
║ └────────────────────────────────────────────────────────────────╢
║ ║
║ PRINT SETTINGS ║
║ ┌────────────────────────────────────────────────────────────────╢
║ │ ║
║ │ Quantity: [ 5 ] tags ║
║ │ ║
║ │ Tag Type: ○ Hang Tag (Apparel) ║
║ │ ● Price Tag (Standard) ║
║ │ ○ Label (Adhesive) ║
║ │ ║
║ │ Printer: [Zebra ZD621R ▼] ║
║ │ ║
║ └────────────────────────────────────────────────────────────────╢
║ ║
║ TAG PREVIEW ║
║ ┌────────────────────────────────────────────────────────────────╢
║ │ ┌─────────────────────────┐ ║
║ │ │ NEXUS CLOTHING │ ║
║ │ │ │ ║
║ │ │ Galaxy V-Neck Tee │ ║
║ │ │ Navy / Medium │ ║
║ │ │ │ ║
║ │ │ $29.00 │ ║
║ │ │ │ ║
║ │ │ ||||||||||||||||||| │ <- Barcode ║
║ │ │ NXJ1078-NAV-M │ ║
║ │ │ │ ║
║ │ │ [RFID ENCODED] │ <- Chip indicator ║
║ │ └─────────────────────────┘ ║
║ │ ║
║ └────────────────────────────────────────────────────────────────╢
║ ║
║ ┌─────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ [PRINT 5 TAGS] │ ║
║ │ │ ║
║ └─────────────────────────────────────────────────────────────┘ ║
║ ║
╚════════════════════════════════════════════════════════════════════╝
ZPL Template
^XA
^FO50,50^A0N,30,30^FDNexus Clothing^FS
^FO50,100^A0N,40,40^FD%PRODUCT_NAME%^FS
^FO50,150^A0N,25,25^FD%VARIANT%^FS
^FO50,200^A0N,50,50^FD$%PRICE%^FS
^FO50,280^BY2^BCN,80,Y,N,N^FD%SKU%^FS
^RFW,H,2,4,1^FD%EPC%^FS
^RFR,H,0,12,1^FN0^FS
^XZ
Template Variables:
| Variable | Source | Example |
|---|---|---|
| %PRODUCT_NAME% | Product.Name | Galaxy V-Neck Tee |
| %VARIANT% | Size/Color | Navy / Medium |
| %PRICE% | Product.Price | 29.00 |
| %SKU% | Product.SKU | NXJ1078-NAV-M |
| %EPC% | Generated | E28011606000020752345 |
16.6 Local Database Schema
-- Sessions (with auto-save checkpoint support)
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
server_session_id TEXT, -- ID from POST /sessions response
location_code TEXT NOT NULL,
count_type TEXT NOT NULL, -- full_inventory, cycle_count, spot_check, find_item
section TEXT, -- assigned section (replaces zone)
operator_id TEXT NOT NULL,
started_at TEXT NOT NULL,
ended_at TEXT,
status TEXT DEFAULT 'active', -- active, paused, completed, cancelled
notes TEXT,
expected_count INTEGER, -- from server, for progress tracking
last_checkpoint_at TEXT, -- auto-save: last SQLite flush timestamp
total_reads_at_checkpoint INTEGER DEFAULT 0, -- reads saved at last checkpoint
interrupted INTEGER DEFAULT 0, -- 1 if app crashed/closed during session
is_joined INTEGER DEFAULT 0, -- 1 if operator joined existing session
synced_at TEXT
);
-- Tag Reads (raw data, deduplicated locally by EPC)
CREATE TABLE tag_reads (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
epc TEXT NOT NULL,
rssi INTEGER,
read_count INTEGER DEFAULT 1, -- times this EPC was read
first_seen_at TEXT NOT NULL,
last_seen_at TEXT NOT NULL,
UNIQUE (session_id, epc), -- one row per EPC per session
FOREIGN KEY (session_id) REFERENCES sessions(id)
);
-- Session Items (aggregated: EPC → SKU resolution)
CREATE TABLE session_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
sku TEXT,
epc TEXT NOT NULL,
product_name TEXT,
quantity INTEGER DEFAULT 1,
expected_qty INTEGER,
status TEXT DEFAULT 'matched', -- matched, over, short, unknown
FOREIGN KEY (session_id) REFERENCES sessions(id)
);
-- Product Cache
CREATE TABLE products (
sku TEXT PRIMARY KEY,
name TEXT NOT NULL,
barcode TEXT,
price REAL,
category TEXT,
last_synced TEXT NOT NULL
);
-- Tag Mappings (EPC prefix → SKU for offline decoding)
CREATE TABLE tag_mappings (
epc_prefix TEXT PRIMARY KEY,
sku TEXT NOT NULL,
last_synced TEXT NOT NULL
);
-- Sync Queue (chunked upload tracking)
CREATE TABLE sync_queue (
id INTEGER PRIMARY KEY AUTOINCREMENT,
entity_type TEXT NOT NULL, -- 'session_chunk', 'session_complete'
entity_id TEXT NOT NULL, -- session_id
action TEXT NOT NULL, -- 'upload_chunk', 'complete'
payload TEXT NOT NULL, -- JSON chunk data
chunk_index INTEGER, -- 0-based chunk number
total_chunks INTEGER, -- total expected chunks for this session
chunks_uploaded INTEGER DEFAULT 0, -- chunks successfully uploaded so far
retry_count INTEGER DEFAULT 0,
created_at TEXT NOT NULL,
status TEXT DEFAULT 'pending' -- pending, uploading, completed, failed
);
16.7 Offline Capabilities
| Feature | Offline Behavior |
|---|---|
| Login | Uses cached credentials |
| Session Start | Creates local session ID |
| Scanning | Full functionality |
| Product Lookup | Uses cached catalog |
| Session Summary | Calculates from local data |
| Submit | Queues for later sync |
| Sync Status | Shows pending items |
Sync Priority
| Priority | Data Type | Frequency |
|---|---|---|
| 1 (Critical) | Completed sessions | Immediate when online |
| 2 (High) | Session chunks | Background chunked upload |
| 3 (Medium) | Product updates | Pull on app launch |
| 4 (Low) | User list | Daily refresh |
Chunked Upload Strategy
Large sessions (100,000+ tag reads) are uploaded in chunks of 5,000 events each:
Session: 47,000 tag reads → 10 chunks
Chunk 0: events[0..4999] → POST /sessions/{id}/chunks ✓
Chunk 1: events[5000..9999] → POST /sessions/{id}/chunks ✓
Chunk 2: events[10000..14999] → POST /sessions/{id}/chunks ✗ (network error)
...retry after reconnect...
Chunk 2: events[10000..14999] → POST /sessions/{id}/chunks ✓ (idempotent)
Chunk 3-9: ...
POST /sessions/{id}/complete → Trigger variance calculation
Resume logic: On network failure, call GET /sessions/{id}/upload-status to identify missing chunks and retry only those. Server deduplicates by (session_id, epc) UNIQUE constraint, making retries safe.
Session Auto-Save & Recovery
Auto-save protects against data loss from app crashes, battery death, or accidental closure.
Auto-Save Triggers:
| Trigger | Action |
|---|---|
| Every 30 seconds (configurable) | Flush tag_reads to SQLite, update last_checkpoint_at |
| Battery ≤ 20% | Force checkpoint + yellow warning |
| Battery ≤ 10% | Force checkpoint + orange warning + “Save & Exit” prompt |
| Battery ≤ 5% | Force checkpoint + auto-pause session |
| App backgrounded | Force checkpoint |
Recovery Flow (on app restart):
App Launch
│
├─── Check: Any sessions WHERE status='active' AND ended_at IS NULL?
│
├── NO → Normal flow → Home Dashboard
│
└── YES → Show Recovery Dialog
┌─────────────────────────────────────────────┐
│ │
│ ⚠️ Interrupted Session Found │
│ │
│ Session: #GM-2024-1229-001 │
│ Type: Full Inventory │
│ Tags Read: 2,847 │
│ Last Save: 3:42 PM (12 min ago) │
│ │
│ ┌─────────────┐ ┌─────────────────────┐ │
│ │ RESUME │ │ DISCARD SESSION │ │
│ └─────────────┘ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────┘
- Resume: Reload cached
tag_reads, reconnect reader, continue scanning from checkpoint - Discard: Mark session
cancelledin SQLite. Data preserved locally but not uploaded to server
Battery Warning Indicators (shown in status bar during scanning):
| Battery | Color | Icon | Action |
|---|---|---|---|
| > 20% | Green | ● | Normal operation |
| 11-20% | Yellow | ▲ | Warning badge, checkpoint forced |
| 6-10% | Orange | ▲▲ | “Save & Exit” prompt |
| ≤ 5% | Red | ◼ | Auto-pause, force checkpoint |
16.8 Configuration Architecture
Where Configuration Lives
┌─────────────────────────────────────────────────────────────────────────────────┐
│ RFID CONFIGURATION ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ TENANT ADMIN PORTAL (app.rapos.com) │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ Settings > RFID │ │
│ │ ├── Devices (claim codes, device list) ← ADMIN CONFIGURES HERE │ │
│ │ ├── Printers (IP addresses, templates) │ │
│ │ ├── Tag Config (EPC prefix, thresholds) │ │
│ │ └── Templates (label designs) │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ CENTRAL API (api.rapos.com) │ │
│ │ ├── GET /api/rfid/config ← Mobile app fetches on startup │ │
│ │ ├── GET /api/rfid/products ← Product catalog cache │ │
│ │ ├── GET /api/rfid/tag-mappings ← EPC → SKU mappings │ │
│ │ └── POST /api/rfid/sessions ← Upload scan sessions │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ RAPTAG MOBILE APP (This Chapter) │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ Local SQLite Database │ │
│ │ ├── cached_config ← Downloaded from API on startup │ │
│ │ ├── products ← Product catalog for offline use │ │
│ │ ├── tag_mappings ← EPC decoding for offline use │ │
│ │ └── sessions ← Locally created, synced when online │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
Configuration Flow
1. DEVICE REGISTRATION (One-time setup)
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Admin Portal│────▶│ Generate │────▶│ Display │
│ Settings │ │ Claim Code │ │ X7K9M2 │
└─────────────┘ └─────────────┘ └─────────────┘
│
▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Scanner │◀────│ Validate │◀────│ Enter Code │
│ Registered │ │ & Activate │ │ on Device │
└─────────────┘ └─────────────┘ └─────────────┘
> **Claim Code**: The code is a 6-character alphanumeric string with 24-hour expiry and one-time use. Before initializing the RFID reader, operators must enter the claim code generated in the Admin Portal. See Chapter 05 Section 5.16.1 for full claim code specifications.
2. ONGOING CONFIGURATION SYNC
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ App Launch │────▶│ Check API │────▶│ Download │
│ │ │ /rfid/config│ │ Updates │
└─────────────┘ └─────────────┘ └─────────────┘
│
▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Update │◀────│ Merge with │◀────│ Store in │
│ Local Cache │ │ Local Data │ │ SQLite │
└─────────────┘ └─────────────┘ └─────────────┘
What’s Configured Where
| Setting | Location | Notes |
|---|---|---|
| EPC Prefix | Tenant Admin > RFID > Tag Config | Read-only in app |
| Variance Threshold | Tenant Admin > RFID > Tag Config | Applied during session summary |
| Auto-Save Interval | Tenant Admin > RFID > Tag Config | Default 30s, synced to app |
| Chunk Upload Size | Tenant Admin > RFID > Tag Config | Default 5,000 events |
| RSSI Threshold | Tenant Admin > RFID > Tag Config | Default -70 dBm, filter phantom reads |
| Claim Codes | Tenant Admin > RFID > Devices | Generated per location |
| Printer IPs | Tenant Admin > RFID > Printers | Synced to app |
| Label Templates | Tenant Admin > RFID > Templates | Synced to app |
| Sections | Tenant Admin > RFID > Locations | Configurable per location |
| Power Level | Mobile App > Settings | User-adjustable per session |
| Sound/Vibration | Mobile App > Settings | User preference |
| Session Notes | Mobile App > Session Start | Per-session |
API Endpoints for Mobile App
// Configuration downloaded on startup
GET /api/rfid/config
Response: {
tenant_id: string,
epc_prefix: string,
variance_threshold: number,
auto_save_interval_seconds: number,
chunk_upload_size: number,
min_rssi_threshold: number,
allow_overrides: boolean,
session_timeout_minutes: number,
printers: [{
id: string,
name: string,
ip_address: string,
location_code: string,
model: string
}],
templates: [{
id: string,
name: string,
type: "hang_tag" | "price_tag" | "label",
zpl_content: string
}],
sections: [{
location_code: string,
sections: string[]
}]
}
// Product catalog for offline use
GET /api/rfid/products
Response: [{
sku: string,
name: string,
barcode: string,
price: number,
category: string
}]
// EPC → SKU mappings for offline decoding
GET /api/rfid/tag-mappings
Response: [{
epc_prefix: string,
sku: string
}]
// Create new counting session
POST /api/rfid/sessions
Body: {
location_code: string,
count_type: "full_inventory" | "cycle_count" | "spot_check" | "find_item",
section: string,
notes: string
}
Response: {
session_id: string,
expected_count: number,
sections_available: string[]
}
// Join existing session as additional operator
POST /api/rfid/sessions/{sessionId}/join
Body: {
operator_id: string,
device_id: string,
assigned_section: string
}
Response: {
session_id: string,
operator_count: number,
your_section: string,
session_started_at: string
}
// Upload scan events in chunks (≤5,000 events per chunk)
// Idempotent: duplicate (session_id, epc) pairs are deduplicated server-side
POST /api/rfid/sessions/{sessionId}/chunks
Body: {
chunk_index: number,
total_chunks: number,
operator_id: string,
device_id: string,
events: [{
epc: string,
rssi: number,
read_count: number,
first_seen_at: string,
last_seen_at: string
}]
}
Response: {
events_accepted: number,
events_deduplicated: number,
chunks_received: number,
chunks_expected: number
}
// Check upload progress (for resume after network failure)
GET /api/rfid/sessions/{sessionId}/upload-status
Response: {
session_id: string,
status: "uploading" | "complete" | "incomplete",
chunks_received: number[],
chunks_missing: number[],
total_events: number,
unique_epcs: number
}
// Complete session and trigger variance calculation
POST /api/rfid/sessions/{sessionId}/complete
Body: {
ended_at: string,
notes: string
}
Response: {
session_id: string,
status: "completed",
variance_percent: number,
review_required: boolean
}
16.9 Summary
The Raptag mobile application provides:
- Fast Authentication: PIN-based login with offline support
- Home Dashboard: At-a-glance view of assigned counts, sync status, and device health
- Flexible Counting: Multiple session types for different inventory needs
- Multi-Operator Sessions: Multiple operators scanning sections in parallel with server-side deduplication
- Real-time Scanning: Live tag counts with progress tracking and signal feedback
- Auto-Save & Recovery: 30-second SQLite checkpoints with crash recovery
- Accuracy Tracking: Variance calculation and discrepancy flagging
- Chunked Sync: Reliable batch uploads (5,000 events/chunk) with idempotent deduplication
- Tag Printing: Integrated label printing with SGTIN-96 encoding
Configuration is managed centrally in the Tenant Admin Portal (Chapter 15), with the mobile app downloading settings on startup. This ensures consistent configuration across all devices and enables remote management without physically accessing scanners.
Document Information
| Attribute | Value |
|---|---|
| Version | 5.0.0 |
| Created | 2025-12-29 |
| Updated | 2026-02-25 |
| Author | Claude Code |
| Status | Active |
| Part | V - Frontend |
| Chapter | 16 of 32 |
This chapter is part of the POS Blueprint Book. All content is self-contained.