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".
RequiresApproval o si la validación post-upload detecta discrepancias en modo AutoPromoteIfMatched.
| 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 |
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.
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.
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.
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.
| 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 |
@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.
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.
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 |
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).
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 |
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 |
429 (rate limited), tool baja a 1 batch a la vezCó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 |
Anti-patterns considerados y descartados.
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).
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.
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.