Discussion Mockups QIIUB · Import Tool
Session: AB7F-K3M2
Q
QIIUB Migration Tool v1.4.2 · up to date
RMH connected | Technician: Carlos M. (Partner: TechSupport PR)
Token validated RMH connection Local analysis complete Pre-validation Upload to QIIUB Approval
Import Token AB7F-K3M2
Target merchant
Farmacia Bayamón Centro
📍 Bayamón, PR 🏷 Qiiub Rx Owner: Juan A. Pérez
Source
RMH 3.2.0
FARMACIA-SRV01\RMH
Filters applied: products with movement since Apr 16, 2024 · sales since Apr 16, 2023. Excluding POs, transfers, adjustments, and open shifts.

Data to import

Products
8,234
of 12,891 in RMH · −4,657 without movement
Customers
3,402
including pending AR balances
Suppliers
187
only active in last year
Sales
128,540
3 years · 2023 → today
Locations found (3 of 4)
Select all
Bayamón Centro (Principal) Code: BAY01 · 2 terminals
$1.24M · 95,201 sales Active
Bayamón Norte Code: BAY02 · 1 terminal
$418K · 28,402 sales Active
Toa Baja Code: TBA01 · 1 terminal
$72K · 4,937 sales Active
Vega Alta (closed 2022) Code: VEG01 · 0 terminals
$0 · 0 sales in period No sales
Tax Rates (pre-configured mapping)
Edit mapping
IVU Estatal 10.5% RMH: TAX_STATE
IVU Estatal PR 10.5% QIIUB: pre-configured
Mapped
IVU Municipal 1% RMH: TAX_MUNI
IVU Municipal PR 1% QIIUB: pre-configured
Mapped
Exempt RMH: TAX_EXEMPT
— not mapped — will be mapped in portal
Pending
Payment methods (5 found)
Edit mapping
Cash RMH: PAY_CASH · 64,238 transactions
Efectivo QIIUB: pre-configured
Mapped
Credit Card (Visa/MC) RMH: PAY_CC_VISAMC · 48,920 transactions
Credit Card QIIUB: pre-configured
Mapped
ATH Móvil RMH: PAY_ATH · 12,340 transactions
ATH Móvil QIIUB: will be created
New
Insurance Copay RMH: PAY_INS · 2,890 transactions
— not mapped — will be mapped in portal
Pending
Charge Account (AR) RMH: PAY_AR · 152 transactions
Charge Account QIIUB: AR module
Mapped
Total size 847 MB compressed: ~210 MB
Batches 412 across 6 entities
Estimated time ~12 min @ 5 Mbps · ETA 14:32
Validation Auto totals comparison
Analysis complete. Ready to submit for approval.
Numbers will be sent to the portal for PartnerAdmin / MerchantAdmin / SystemAdmin to review. Upload only starts after approval.
Tool view — post-analysis pre-upload screen. Default language: English. The technician can toggle to Spanish via the EN/ES button in the titlebar. Totals (8,234 products, 128,540 sales) are exactly the values that will appear in the Portal during upload — that's the verification guarantee.
Migración Sesiones de import AB7F-K3M2 — Aprobación de plan
JF
José J. Flores
SystemAdmin · MigrationApprover
Gate 1 — Aprobación previa al upload
El técnico envió este plan para tu revisión. Si los números se ven bien, aprueba para que pueda iniciar el upload de ~847 MB. Si algo no cuadra, rechaza con un comentario y el técnico re-analizará. Nada se ha subido todavía.

Totales propuestos por el tool

Productos
8,234
de 12,891 en RMH · −4,657 sin movimiento
Customers
3,402
incluyendo AR balances
Suppliers
187
activos último año
Ventas
128,540
3 años · 2023 → hoy
Filtros aplicados: productos con movimiento desde 16 abr 2024 · ventas desde 16 abr 2023. POs, transferencias, ajustes y turnos abiertos no se importan.
Locations a crear (3 de 4)
Bayamón Centro (Principal) Code: BAY01 · 2 terminales
$1.24M Activa
Bayamón Norte Code: BAY02 · 1 terminal
$418K Activa
Toa Baja Code: TBA01 · 1 terminal
$72K Activa
Vega Alta excluida por el técnico (sin ventas)
$0 Excluida
Resumen de mappings
Tax Rates
2 mapped 1 pending
3 total
Métodos de pago
3 mapped 1 new 1 pending
5 total
Categories
42 new
42 total
2 mappings pendientes requieren tu atención post-upload (no bloquean Gate 1, pero deben resolverse antes de Gate 2).
Tamaño total del upload 847 MB comprimido: ~210 MB
Batches 412 6 entidades
Tiempo estimado ~12 min @ 5 Mbps
Modo de activación Manual Aprobación manual requerida
Decisión sobre el plan
Si los números se ven correctos, aprueba para autorizar el upload. El técnico recibirá la confirmación en el tool y podrá iniciar la subida cuando esté listo. Si algo no cuadra, rechaza con un comentario y el técnico re-analizará localmente sin transferir nada.
Gate 1 Approval view — pantalla del portal cuando un plan está pendiente de aprobación. La sidebar muestra un badge amarillo en "Sesiones de import" señalando que hay algo que requiere atención. Esta vista la ven PartnerAdmin, MerchantAdmin (si existe), o SystemAdmin — cualquiera con permiso MigrationApprover. Los mappings pendientes son visibles pero no bloquean Gate 1 (se pueden resolver post-upload antes de Gate 2). Al aprobar, el tool del técnico detecta la aprobación (polling) y habilita el botón "Start upload".
Migración Sesiones de import AB7F-K3M2
JF
José J. Flores
SystemAdmin
Progreso de subida
47%
Tiempo restante
~6 min
193 / 412 batches subidos 398 MB / 847 MB Velocidad: 4.8 Mbps Iniciado: 14:18 AST

Totales esperados (pre-calculados por el tool)

Productos
8,234
subidos: 8,234
Customers
3,402
subidos: 3,402
Suppliers
187
subidos: 187
Ventas
128,540
subidos: 60,420
Progreso por entidad
refresca cada 2s
Locations 3 batches · 3 locations · completado 14:19:02
3 / 3 100%
Reference data (taxes, payments) 2 batches · completado 14:19:14
2 / 2 100%
Suppliers 1 batch · 187 records · completado 14:19:22
1 / 1 100%
Products 17 batches · 8,234 records · completado 14:21:48
17 / 17 100%
Customers 7 batches · 3,402 records · completado 14:23:11
7 / 7 100%
Sales (header + lines + payments) en progreso · batch 163 / 382
163 / 382 43%
AR Ledger esperando · 152 records
0 / 2
Loyalty / Gift Cards esperando · 412 records
0 / 1
Sesión
Token AB7F-K3M2
Iniciado por Carlos Méndez Partner: TechSupport PR
Tool version v1.4.2
Source RMH 3.2.0 FARMACIA-SRV01\RMH
Modo de activación Manual Aprobación manual requerida
Plan aprobado por Carlos Méndez (PartnerAdmin) 14:17 AST
Subida iniciada 14:18 AST
ETA finalización ~14:30 AST
Final activation approval
Available when upload finishes. On approval, the imported data goes live for the merchant: POS terminals can operate, employees can access the portal, and the merchant becomes Active. This merchant is configured to require manual approval. If it were configured for auto-activation when matched, this step would be skipped automatically when validation shows 100% match.
Mappings pendientes: hay 2 items sin mapear (Tax: Exempt; Payment: Insurance Copay). Resuélvelos antes de aprobar.
Portal view — pantalla de seguimiento durante la subida. Default ES (mercado PR), toggle EN/ES en el topbar. Esta vista corresponde a la fase posterior a Gate 1 (plan ya aprobado por PartnerAdmin a las 14:17) y durante el upload activo. Gate 2 abajo está deshabilitado hasta que termine la subida; será requerido solo si el merchant está en modo RequiresApproval o si la validación post-upload detecta discrepancias en modo AutoPromoteIfMatched.
Migración Sesiones de import AB7F-K3M2 — Validación
JF
José J. Flores
SystemAdmin
Cuadre verificado: 8 entidades · 0 discrepancias
Todos los totales pre-calculados por el tool coinciden exactamente con los registros activados para el merchant. La migración está lista para activar.
Entidad Esperado (Tool) Real (activado) Δ Estado
Locations 3 3 0 ✓ Match
Tax Rates 3 3 0 ✓ Match
Payment Methods 5 5 0 ✓ Match
Suppliers 187 187 0 ✓ Match
Categories 42 42 0 ✓ Match
Products (variants) 8,234 8,234 0 ✓ Match
Customers 3,402 3,402 0 ✓ Match
Sales (headers) 128,540 128,540 0 ✓ Match
Sale Line Items 412,892 412,892 0 ✓ Match
Sale Payments 128,540 128,540 0 ✓ Match
AR Ledger entries 2,418 2,418 0 ✓ Match
Total $ ventas $1,737,402.18 $1,737,402.18 $0.00 ✓ Match
Total $ AR balance $28,491.50 $28,491.50 $0.00 ✓ Match
Activar merchant
Al activar, el merchant pasa de ImportingActive. Los terminales POS podrán autenticarse, los usuarios podrán acceder al portal del merchant, y se programa cleanup de los datos temporales en 7 días.
Validación final — pantalla del portal después de aprobar la activación. User-facing dice "Validación final"; internamente compara PrecomputedTotals (lo que el tool envió) vs PostImportTotals (lo que efectivamente se activó). Si todo cuadra, se habilita "Activar merchant". Si hay discrepancias, el botón queda deshabilitado y aparece un panel de error con detalle por entidad.

End-to-End Flow

Las 7 fases del proceso de migración completo, desde la creación del merchant hasta su activación. Cada fase indica el actor responsable y el estado del merchant en ese momento.

1 Crear merchant en portal BCPOS / Partner
Acción
Crear merchant con flag OriginType = ImportFromRmsRmh
Sistema genera
Import Token de 8 chars
AB7F-K3M2
Estado del merchant
Importing
bloqueado de ops normales
2 Técnico ejecuta el tool en cliente Técnico (Partner)
Validación de versión
Tool v1.4.2 → backend → ✓ al día
Login + token
Partner credentials + tipea AB7F-K3M2
Confirmación visual
"¿Importar para Farmacia Bayamón Centro?" → Confirmar
3 Análisis local (RMS/RMH → SQLite) Tool · local
Connection string
FARMACIA-SRV01\RMH
Filtros aplicados
Productos con movimiento últimos 2 años · ventas hasta 3 años
Output
SQLite local con staging tables + sumarios pre-calculados
4 Pre-validación + envío del plan Técnico (Partner)
Dashboard del tool
Stats por entidad, locations seleccionables, mappings de taxes/payments
Decisiones del técnico
Excluir locations sin ventas, validar mappings, revisar totales
Acción final
Click "Submit plan for approval"
envía solo los sumarios (KB, no MB)
5 🚪 Gate 1 — Aprobación del plan PartnerAdmin / MerchantAdmin / SystemAdmin
Qué se aprueba
Los números prometidos: 8,234 productos, 128,540 ventas, locations seleccionadas, mappings
Quién aprueba
Cualquier user con permiso MigrationApprover
Resultado
Aprobado → tool habilita "Start upload"
rechazado → técnico re-analiza o cancela
6 Upload a ImportDB (resumable) Tool ↔ Cloud
Mecánica
Batches deterministas con IDs idempotentes
Products-Batch-0042
Checkpoint tracking
ImportSession.Checkpoints JSON · resume tras crash
Estado del merchant
Uploading
visible en portal
7 🚪 Gate 2 — Aprobación de promoción (configurable per-merchant) MigrationApprover
Modo RequiresApproval (default)
Siempre se requiere aprobación manual. Para clientes grandes / regulados / complejos.
Modo AutoPromoteIfMatched
Si validación = 100% match → auto-promote. Si discrepancia → cae a manual (safety net).
Quién configura el modo
SystemAdmin al crear el merchant
no el partner — evita que todos elijan express
8 Validación final + activación Sistema / SystemAdmin
Validación automática
Compara PrecomputedTotals vs PostImportTotals fila por fila
Activar merchant
Cleanup de ImportDB programado en 7 días · POS terminals pueden autenticarse
Estado del merchant
Active
listo para operar
Diagrama end-to-end (8 fases) — los 2 gates de aprobación están marcados en amarillo. Gate 1 (Fase 5) es siempre obligatorio: alguien del lado del cliente o partner aprueba los números antes de que se transfieran GBs. Gate 2 (Fase 7) es configurable per-merchant: RequiresApproval (default, siempre manual) o AutoPromoteIfMatched (auto si valida 100%, manual si hay discrepancia). El modo del Gate 2 lo escoge SystemAdmin al crear el merchant — no el partner.

Tech stack del ejecutable

Comparación de las 6 opciones evaluadas. La recomendación final considera Claude Code como dev principal, reuso de design system, performance de SQL Server, y simplicidad de deployment.

Blazor Hybrid (WPF host)
"Single-language all-.NET option"
★★★★ 4.2 / 5
  • Host: WPF or .NET MAUI
  • UI: Razor components + Tailwind
  • SQL: Microsoft.Data.SqlClient
  • Pure C# — single language
  • Can share design tokens (tokens.css)
  • Claude Code very strong with Razor
  • Cannot share React components from @qiiub/ui
  • Requires re-implementing UI primitives
  • Slightly heavier than Photino
Tauri + React
"Already used by team for another project"
★★★★★ 3.5 / 5
  • Host: Tauri (Rust core)
  • UI: React + Tailwind
  • SQL: Rust crates or .NET sidecar
  • Reuses @qiiub/ui literally
  • Tiniest bundle (~10MB)
  • Team has prior experience
  • Adds Rust as 3rd language
  • SQL Server less mature in Rust ecosystem
  • Claude Code slower in Rust vs C#
  • Tauri toolchain adds build complexity

Comparativa completa — todas las opciones evaluadas

Opción Reuso de @qiiub/ui SQL Server fit Bundle size Claude Code fit Score
.NET 10 + Photino + React Literal reuse Excellent ~50 MB 5/5 5.0
Blazor Hybrid (WPF host) Tokens only Excellent ~80 MB 5/5 4.2
Tauri + React Literal reuse Via sidecar ~10 MB 3/5 (Rust) 3.5
.NET + WebView2 directo Literal reuse Excellent ~50 MB 4/5 4.0
WPF (XAML puro) None Excellent ~70 MB 3/5 (XAML) 2.5
WinUI 3 None Excellent Variable 2/5 2.0
Decisión recomendada
.NET 10 + Photino + React reusando componentes de @qiiub/ui. Esto da el técnico una experiencia visualmente idéntica al portal, mantiene el equipo en lenguajes que ya conoce (C# + TypeScript), y aprovecha que Claude Code es excepcionalmente fuerte en ambos. Si el equipo prefiere puramente .NET sin TypeScript, Blazor Hybrid es la alternativa fuerte (4.2/5) — pierde el reuso de componentes pero gana simplicidad de stack.
Tech stack comparison — punto clave de discusión. Antes de comprometernos a Photino, vale la pena verificar dos cosas: (1) que Steve Sanderson siga manteniendo activamente el proyecto, y (2) que el patrón de comunicación WebView ↔ .NET (postMessage) escale bien para los volúmenes que vamos a manejar (subir batches de varios MB). Si alguno de los dos da problema, la fallback natural es Blazor Hybrid.

Mecanismo de upload

Cómo el tool entrega físicamente los batches de data al cloud. Tres opciones evaluadas. Esta decisión es candidata a ADR independiente cuando se implemente.

Decisión recomendada

B — API endpoints por batch

Tool POST cada batch al API principal de QIIUB en un endpoint group separado (timeouts y body limits propios). Payload MessagePack + Brotli, idempotente por BatchId, audit trail completo. Sin SQL credentials expuestas, sin infraestructura adicional. A nuestra escala (1,000 merchants × 1 migración cada uno) C es over-engineering y A es inseguro.

A
SQL directo
Tool conecta a Azure SQL ImportDB con SqlBulkCopy
  • SQL credentials expuestas en el cliente — un técnico con acceso al .exe las extrae
  • Firewall Azure SQL abierto al IP del cliente (1,000 IPs cambiantes — imposible de manejar)
  • Técnico curioso puede ejecutar SQL arbitrario contra ImportDB
  • Auditoría = cero
  • Sin reglas de negocio entre tool y DB (validación, idempotencia, rate limiting)

Non-starter de seguridad. No se discute.

C
Blob storage + background processor
Tool sube blobs comprimidos a Azure Blob Storage vía SAS URL. Background worker (Function/Container Apps Job) lee y procesa
  • Tool termina rápido (solo upload, no espera proceso)
  • Resiliente a restart de API
  • Worker escala independiente
  • Blob storage + Function + queue + coordinación = infra significativa
  • Auth: JWT + SAS tokens (más complejo)
  • Idempotencia requiere coordinar worker y API
  • Debug en producción: difícil — múltiples sistemas
  • Costo Azure: storage + Function + queue
  • Implementación: semanas

Miles de imports concurrentes · uploads de gigabytes · necesidad de subir aunque el API esté caído. Ninguno aplica para QIIUB v1.

Comparativa detallada B vs C

Por qué B gana sobre C a nuestra escala específica.

Criterio B — API por batch C — Blob + background
Complejidad infra Baja (un endpoint group más) Alta (blob + worker + queue + coordinación)
Latencia percibida por técnico Espera procesamiento (~500ms/batch) Tool termina rápido (solo upload)
Carga sobre API principal Sí — endpoint pesado No — solo metadata
Resilience a restart de API Batch falla, tool retry (Polly) Blob persiste, worker continúa
Debuggability Trivial — una request, un log Difícil — múltiples sistemas
Auth model JWT estándar del proyecto JWT + SAS tokens
Idempotencia Trivial por BatchId Coordinar worker y API
Time-to-implement Días Semanas
Costo Azure (steady state) Solo compute API existente + Blob + Function + Queue

Diseño del endpoint

Forma del request y response. Implementado como FastEndpoints en endpoint group separado del API principal con timeouts y body limits propios (60s timeout, 50MB body cap).

Request

POST /api/v1/imports/{token}/batches/{batchId} Content-Type: application/x-msgpack Content-Encoding: br Authorization: Bearer {jwt} X-Batch-Checksum: sha256:{hex} [binary MessagePack + Brotli payload]

Response — éxito normal

HTTP/1.1 200 OK Content-Type: application/json { "batchId": "Products-Batch-0042", "received": "2026-04-17T18:32:11.234Z", "rowsInserted": 500, "checkpoints": { "Products": { "lastCompletedBatch": 42, "totalBatches": 100 } } }

Response — dedupe idempotente (batch ya recibido)

HTTP/1.1 200 OK Content-Type: application/json { "batchId": "Products-Batch-0042", "alreadyProcessed": true }

Stack del payload

Cada capa elegida por una razón específica. Todo el stack ya existe en el proyecto QIIUB.

Capa Tecnología Justificación
Wire format MessagePack 2-5x más chico que JSON. Ya está en stack QIIUB
Compresión Brotli (level 6) 15-20% mejor que gzip. Native en .NET
Streaming Stream end-to-end Nunca materializar batch completo en memoria
DB insert SqlBulkCopy directo a ImportDB 50ms vs 2s vs EF Core para 1,000 rows
Idempotencia WHERE NOT EXISTS (BatchId) Server descarta duplicados silenciosamente
Integrity check SHA-256 en header Detectar corrupción en tránsito

Tamaño de batch

Calibrado para batches comprimidos de 1-5 MB, transferencia individual <1s en conexión típica.

Entidad Rows por batch Razonamiento
Products, Customers, Suppliers 500 Records pequeños, alta fan-out
Categories, Tax, Payment, Locations All-in-one Volumen total bajo
Sales (header + lines + payments) 200 sales Mantener atomicidad de la venta dentro del batch
AR Ledger 500 Records simples

Concurrencia desde el tool

  • Tool sube 3-5 batches en paralelo (no 1, no 50)
  • Polly retry con exponential backoff sobre 5xx y network errors
  • Si API responde 429 (rate limited), tool baja a 1 batch a la vez

Controles de seguridad

Cómo B mantiene seguridad equivalente o superior a alternativas más complejas.

Control Implementación
Sin SQL credentials expuestas Tool solo conoce HTTPS + JWT del API
Auth normal del proyecto JWT con claim MigrationOperator + token de import válido en el path
Validación server-side Token existe · sesión activa · merchant en Importing · operator tiene permiso · BatchId está dentro del rango pre-declarado
Audit trail Serilog estructurado con MerchantId, ImportToken, BatchId, byte count, checksum
Rate limiting per-token Máximo N batches/minuto por ImportToken — evita abuse
Body size limit 50 MB hard cap — batches más grandes se rechazan
Integrity SHA-256 checksum verificado server-side antes de insertar

Lo que NO hacemos (explícitamente)

Anti-patterns considerados y descartados.

  • Un solo ZIP gigante al final — perdemos resumability fina, perdemos progreso visible, retry cuesta GBs
  • WebSockets / SignalR — overkill, request-response simple basta
  • Multipart form-data — peor que body binary directo
  • gRPC — sería elegante pero agrega un transport más al proyecto

Modelo de upload trigger

Cuándo arranca la subida real.

Decisión v1: upload solo cuando el técnico hace click en "Start upload" (no progresivo durante el análisis).

  1. Da control claro al técnico
  2. Si decide cancelar tras revisar el dashboard, no se subió nada
  3. Simplifica el modelo mental ("revisé → confirmé → subiendo")

Si en el futuro queremos modo "upload progresivo" donde el tool empieza a subir locations/reference data mientras el técnico revisa el dashboard, se agrega como opción avanzada sin romper el contrato.

Upload mechanism — esta decisión es candidata a su propio ADR cuando se implemente. Propuesta de título: ADR-00XX — Import data upload via API endpoints, not blob storage. Las decisiones de tamaño de batch y stack del payload pueden ajustarse durante implementación basado en performance real, pero la elección B vs C debe quedar fija desde el principio porque cambia toda la infraestructura.