Saltar a contenido

Tareas

Documento histórico

Este documento se conserva solo para trazabilidad. Consulte el índice maestro para la fuente vigente.

# Tareas pendientes

Documento histórico — auditoría 2026-06-04

Este archivo contiene el plan de trabajo y los hallazgos de la auditoría del 2026-06-04, con extensiones del 2026-06-07. Para conocer el estado actual vigente de la APK, consultar estado-actual.md.

Checklist priorizado con todo lo identificado durante la auditoría, actualizado al 2026-06-04. Cada tarea tiene un ID, descripción, prioridad, archivos afectados y criterios de aceptación.

Cambio importante: a partir de la auditoría del 4 de junio de 2026, el archivo se reorganizó en tres bloques: (1) tareas ✅ completadas desde la última auditoría, (2) tareas ⏳ vigentes que siguen pendientes, (3) tareas 🆕 nuevas identificadas en esta revisión. La doc original cubría 27 tareas; este nuevo estado refleja el avance real del repositorio.


Leyenda de prioridades

  • 🔴 Crítica: bloquea el uso de la app o tiene impacto directo en el usuario.
  • 🟠 Alta: mejora importante de UX o de coherencia con la doc.
  • 🟡 Media: mejora de calidad de código o de extensibilidad.
  • 🟢 Baja: cosmético, limpieza o refactor sin impacto funcional.

✅ Bloque A — Tareas completadas desde la última auditoría

Estas tareas ya no están pendientes; se documentan para mantener la trazabilidad y como referencia.

✅ T-001: Crear los 4 drawables faltantes

Resolución: los 4 drawables están creados como PNG en app/src/main/res/drawable/:

  • ic_islabox_logo.png — isotipo: palmera azul en círculo con isla roja
  • ic_islabox_logo2.png — logo horizontal (palmera + texto)
  • ic_islabox_letra.png — solo texto "isla|box"
  • ic_islabox_success.png — sello/badge de éxito con check

IslaImages.kt apunta a los 4 IDs y WelcomeScreen, SuccessScreen, IslaDashboardTopBar e IslaTopBar los consumen sin crashear. La doc original sugería VectorDrawable (.xml); terminó siendo PNG. No bloquea y el visual es correcto.


✅ T-002: Corregir el identificador del casillero hardcodeado

Resolución: CurrentUser.kt ahora usa lockerId = "CP-001042-8" (correcto por Mod-10). DashboardScreen y PerfilScreen lo muestran sin hardcodear. Coincide con la doc.

La decisión sobre valores definitivos (días gratis, costo por día, etc.) se documenta en ../../../roadmap/preguntas.md.

La decisión sobre valores definitivos (días gratis, costo por día, etc.) se documenta en ../../../roadmap/preguntas.md.


✅ T-003: Declarar permisos faltantes en el AndroidManifest

Resolución: AndroidManifest.xml declara los 5 permisos:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
    android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-feature android:name="android.hardware.camera" android:required="false" />

✅ T-101: Crear FakeDataSource central con datos de muestra

Resolución: app/src/main/java/com/example/islabox/data/local/FakeDataSource.kt existe como object con:

  • currentUser (1 User con KYC status)
  • recipients (3 destinatarios en Cuba)
  • reports (5 reportes con mezcla de estados)
  • serviceRequests (2 solicitudes iniciales, lista mutable)
  • charges, invoices, quotes (datos auxiliares)
  • packageSummary calculado dinámicamente desde reports (no hardcoded)

✅ T-102: Quitar datos hardcodeados de cada pantalla

Resolución: DashboardScreen y PerfilScreen ahora leen de CurrentUser.user. El packageSummary se calcula en FakeDataSource. RemitenteSelector recibe userName por parámetro. CurrentUser es un object global, no un mutableStateOf local.


✅ T-103: Iniciales del avatar calculadas

Resolución: User.kt define una propiedad derivada:

val initials: String get() = "${firstName.firstOrNull()?.uppercaseChar() ?: ""}${lastName.firstOrNull()?.uppercaseChar() ?: ""}"

ProfileHeader recibe initials por parámetro. Para "Kleudy Victor Torno Peña" muestra "KT".


✅ T-104: Decidir entre separar Reporte o mantenerlo unificado

Resolución: se eligió la Opción C — Híbrido. Reporte ahora tiene:

  • purchaseStatus: PurchaseReportStatus (estados administrativos)
  • packageStatus: PhysicalPackageStatus? (estados físicos, nullable para prealertas sin paquete)

Además existe una propiedad de conveniencia estado: String que prioriza el estado físico sobre el administrativo, para mantener compatibilidad con UI previa.


✅ T-105: Cambiar Reporte.estado: String por enums

Resolución: se creó el paquete app/src/main/java/com/example/islabox/domain/enums/ con enums principales:

  • KycStatus (9 valores)
  • DocumentType (3 valores)
  • PurchaseReportStatus (7 valores — la doc define 8, se documenta la diferencia en T-405)
  • PhysicalPackageStatus (10 valores)
  • ConsolidationStatus (8 valores, exactamente alineados con la doc)
  • PaymentStatus (5 valores)
  • ShipmentStatus (16 valores con step para timeline)
  • ServiceType y ServiceStatus (en ServiceRequest.kt)
  • Acceptance y RiskLevel (en CustomsCategory.kt)
  • LockerCodeStatus (en LockerCode.kt)

✅ T-106: Crear LockerCode data class con algoritmo Mod-10

Resolución:

  • LockerCode.kt — data class @Serializable con prefix, sequential, checkDigit, fullCode, normalized, status y enum LockerCodeStatus { ACTIVE, INACTIVE, BLOCKED }.
  • LockerCodeGenerator.kt — implementa calculateCheckDigit, normalize, validate (sealed con Valid / Invalid(reason)), format y generate.
  • Tests unitarios pendientes (ver T-410).

✅ T-107: Crear KycRecord data class

Resolución: KycRecord.kt cubre los 13 campos de la doc, incluyendo submittedAt, expiresAt (10 años), reviewedBy, reviewedAt, rejectionReason. CurrentUser.user ahora incluye kycStatus: KycStatus y se muestra en el dashboard mediante KycBanner.


✅ T-108: Crear ServiceRequest data class

Resolución: ServiceRequest.kt con 4 ServiceType (Inspección Visual, Prueba Funcional, Reempaque, Devolución) y 10 ServiceStatus. Lista de serviceRequests en FakeDataSource.


✅ T-109: Crear CustomsCategory con metadata

Resolución: CustomsCategory.kt con las 15 categorías operativas, cada una con code, name, description, accepted, requiresInvoice, requiresReview, riskLevel, allowsAir, allowsSea, dutyPercentage, userMessage, operatorMessage. Las diferencias con la doc de aduana (ej. "electrodomésticos" sin equivalente directo) están documentadas en T-405.


✅ T-110: Crear Consolidation + ConsolidationPackage

Resolución: ambas data classes existen. Consolidation con 8 estados y campos totalWeight, totalValue, packageCount, shippingAddressId, trackingNumber. ConsolidationPackage con la relación N:N. Falta: pantalla de creación de consolidación (ver T-406).


✅ T-111: Crear entidades de dinero (Quote, Payment, Charge, Invoice)

Resolución: las 4 entidades existen en domain/model/:

  • Quote con QuoteItem (subtotales, descuentos, expiración)
  • Payment con PaymentMethodMix (desglose real/credit/card)
  • Charge (categorías: shipping, insurance, storage, customFees, etc.)
  • Invoice con List<Charge>, List<Payment>, dueDate, paidAt

Falta: pantallas de pago, cotización y factura (ver T-404, T-406, T-407).


✅ T-202: Mover datos del usuario a constantes reutilizables

Resolución: AppSettings.kt existe en core/config/ y centraliza:

// ⚠️ Tarea abierta: actualizar a los valores resueltos (ver [preguntas.md](../../../roadmap/preguntas.md))
const val FREE_STORAGE_DAYS = 5      // debe ser 15
const val STORAGE_DAILY_COST = 1.00  // debe ser 0.50
const val STORAGE_WARNING_DAYS = 1

Decisión resuelta 2026-06-06 (preguntas.md): - FREE_STORAGE_DAYS15 - STORAGE_DAILY_COST0.50

Tanto StorageInfoCard como StorageControl consumen estas constantes.


✅ T-203: Decidir qué hacer con los componentes envios/ huérfanos

Resolución: la carpeta ui/components/envios/ ya no es huérfana. ShipmentTimeline está integrado en ShipmentDetailScreen.kt. Falta: navegar desde el dashboard (ver T-403).


✅ T-204: Eliminar pantalla huérfana ReporteScreen (parcial)

Resolución: ReporteScreen.kt ya no aparece en el grafo de navegación ni en list_dir ui/screens/main. ReportesScreen lo reemplazó. Verificar que el archivo no exista físicamente (ver T-411).


✅ T-205: Eliminar SampleModel y SampleRepository (parcial)

Resolución: SampleRepository ya no existe. Falta confirmar que SampleModel.kt también se haya borrado (ver T-411).


✅ T-206: Unificar días gratis de almacenaje

Resolución parcial (código): StorageInfoCard y StorageControl ahora leen de AppSettings.FREE_STORAGE_DAYS. Ya no hay desincronización entre componentes.

Pendiente (decisión de negocio resuelta 2026-06-06): actualizar el valor de la constante a 15 días y STORAGE_DAILY_COST a $0.50 USD según preguntas.md. Tarea nueva derivada: T-206b — actualizar AppSettings.kt y verificar que las pruebas (StorageInfoCard, StorageControl) sigan pasando. (Ver criterios de almacenaje.)


✅ T-208: Reemplazar paso de Reporte completo en URL por id

Resolución: Screen.ReporteDetalle ahora es:

@Serializable data class ReporteDetalle(val reporteId: String) : Screen()

ReporteDetalleScreen busca el reporte en FakeDataSource por id y muestra un Reporte() vacío si no se encuentra (deep link seguro).


✅ T-209: Mover OtpInputField a common/

Resolución: OtpInputField.kt está en ui/components/common/ y se importa desde VerificationCodeScreen.


✅ T-210: Implementar validaciones inline en el formulario de reporte

Resolución: ReporteFormValidations.kt implementa validateTracking (regex ^[A-Z0-9]{5,}$), validateValor (número > 0) y validateFecha (formato DD/MM/AAAA con validación de días por mes, incluyendo bisiestos). Los errores se muestran inline y el botón "Enviar" se deshabilita.


✅ T-301 a T-306: Pantallas KYC completas

Resolución: las 7 pantallas del flujo KYC están implementadas y conectadas: KycWelcomeScreen, KycDocumentTypeScreen, KycDocumentFrontScreen, KycDocumentBackScreen, KycSelfieScreen, KycResidenceScreen (con las 16 provincias de Cuba) y KycReviewScreen (+ KycSubmittedScreen). KycCamera.kt gestiona el permiso de cámara y simula la captura (integración CameraX pendiente en T-408). El KycBanner se muestra en el dashboard con copy distinto según el estado.


✅ T-401: Decidir la sección "Envíos" del Dashboard

Resolución: se eligió la Opción A — 4 secciones. PackageSummarySection muestra 4 contadores (En almacén, Enviados, En tránsito, Entregados) y cada uno navega a PaquetesScreen con el filtro correspondiente. PaquetesFilter es un enum con ALL, IN_TRANSIT, IN_WAREHOUSE, IN_SHIPMENT, DELIVERED y su método matches(reporte).


✅ T-402: Pantalla de detalle de envío

Resolución: ShipmentDetailScreen.kt está creada y usa el ShipmentTimeline con los 16 estados del enum ShipmentStatus. Muestra tienda, tracking, estado actual, info de aduana (cuando status >= EN_ADUANA_DESTINO), costo final y fecha estimada. Falta: navegación desde la lista de paquetes o desde ReporteDetalleScreen (ver T-403).


✅ T-501: Lista de servicios solicitados

Resolución: ServiceRequestsListScreen.kt lista los ServiceRequest del usuario con filtros por ServiceType y ServiceStatus mediante FilterChip.


✅ T-502: Pantalla de solicitar inspección

Resolución: RequestInspectionScreen.kt permite crear un nuevo ServiceRequest con tipo, descripción y fotos. Crea el request con status = REQUESTED y lo añade a FakeDataSource.serviceRequests.


✅ T-503: Pantalla de solicitar devolución

Resolución: RequestReturnScreen.kt está creada con el mismo patrón que RequestInspectionScreen. Navega desde ReporteDetalleScreen.


✅ ProGuard: Reglas para kotlinx-serialization

Resolución: proguard-rules.pro ya tiene las reglas mínimas para mantener $Companion, $serializer y INSTANCE de las clases @Serializable cuando se activa isMinifyEnabled = true en release.


✅ Migración a Java 17

Resolución: compileOptions en app/build.gradle.kts usa JavaVersion.VERSION_17 (la doc mencionaba Java 11, pero la implementación migró a 17). Esto es coherente con AGP 9.x y Compose Compiler 2.2.10.


⏳ Bloque B — Tareas vigentes (aún pendientes)

Estas tareas estaban en la doc anterior y siguen sin resolverse.

🔴 T-201: Implementar handler de onRegisterClick

Por qué: RegisterScreen llama a onRegisterClick(name, email, phone, password) pero el NavGraph lo ignora (handler { _, _, _, _ -> }). El usuario queda en la pantalla de registro sin avanzar.

Archivos a modificar:

  • app/src/main/java/com/example/islabox/ui/navigation/NavGraph.kt — en el bloque composable<Screen.Register>.

Cambio:

composable<Screen.Register> {
    RegisterScreen(
        onRegisterClick = { _, _, _, _ ->
            // MVP: navegar directamente a Main, simulando registro exitoso.
            // Cuando haya backend, llamar al endpoint y mostrar Success/Error.
            navController.navigate(Screen.Main) {
                popUpTo(Screen.Welcome) { inclusive = true }
            }
        },
        ...
    )
}

Criterios de aceptación:

  • Click en "Registrarse" navega a Main.
  • El usuario ve el dashboard con el banner de KYC (porque sigue sin KYC).
  • No hay crashes ni pantallas en blanco.

🟠 T-403: Conectar ShipmentDetailScreen desde PaquetesScreen y ReporteDetalleScreen

Estado al 2026-06-07: ✅ Resuelto. MainScreen.kt:54-65 redirige a ShipmentDetail cuando packageStatus == DESPACHADO. La ruta Screen.ShipmentDetail(val reporteId: String) está registrada en NavGraph.kt y ShipmentDetailScreen parsea el ShipmentStatus desde el string.

Por qué: la pantalla existe pero no es accesible desde ningún lado. El usuario no puede ver el detalle de un envío.

Archivos a modificar:

  • app/src/main/java/com/example/islabox/ui/navigation/NavGraph.kt — registrar la ruta.
  • app/src/main/java/com/example/islabox/ui/screens/main/PaquetesScreen.kt — pasar el Shipment correspondiente.
  • app/src/main/java/com/example/islabox/ui/screens/reporte/ReporteDetalleScreen.kt — botón "Ver envío".

Cambio sugerido:

@Serializable data class ShipmentDetail(val reporteId: String) : Screen()

Criterios de aceptación:

  • Click en un paquete con packageStatus = DESPACHADO abre su ShipmentDetailScreen. ✅
  • El ShipmentDetailScreen muestra el ShipmentTimeline con el paso actual. ✅
  • Botón back navega a la lista anterior. ✅

🟠 T-404: Pantallas de pago y cotización

Estado al 2026-06-07: ✅ Resuelto. Las 3 pantallas existen: - QuoteScreen.kt muestra cotización con desglose y botón "Aceptar". - PaymentScreen.kt con 3 métodos (Efectivo/Crédito/Tarjeta), desglose en tiempo real, validación de monto completo, navega a Invoice. - InvoiceScreen.kt con detalle de cargos, icono de éxito, navega a Main.

Navegación: Quote → Payment → Invoice → Main. Todo con datos mockeados de FakeDataSource. Falta integración real con Stripe (T-422).

Por qué: los modelos Quote, Payment, Invoice existen pero no hay UI.

Archivos a crear:

  • app/src/main/java/com/example/islabox/ui/screens/servicios/QuoteScreen.kt — muestra una Quote con desglose.
  • app/src/main/java/com/example/islabox/ui/screens/servicios/PaymentScreen.kt — selecciona método (real/credit/card) y confirma.
  • app/src/main/java/com/example/islabox/ui/screens/servicios/InvoiceScreen.kt — lista de cargos + pagos.

Criterios de aceptación:

  • El usuario puede ver el desglose de un envío antes de pagar. ✅
  • Puede confirmar el pago y ver la factura resultante. ✅
  • El paymentMethodMix se desglosa correctamente. ✅
  • Procesar cargo real (T-422).

🟠 T-405: Alinear CustomsCategory y Reporte.estado con la doc

Por qué: hay pequeñas diferencias entre los enums de la app y los definidos en datos/estados.md y datos/aduana.md.

Diferencias detectadas:

Doc (datos/estados.md) App actual Acción
borrador (8 estados prealerta) No implementado en PurchaseReportStatus Agregar DRAFT
reportada_sin_tracking Renombrado a PENDIENTE_TRACKING Renombrar para alinear
tracking_actualizado Renombrado a TRACKING_RECIBIDO Renombrar para alinear
vinculada_a_paquete No existe en PurchaseReportStatus Agregar
cancelada_por_usuario CANCELADA Renombrar
cancelada_por_operador No existe Agregar
requiere_correccion Renombrado a ERROR_DATOS Renombrar
16 estados de physical_packages 10 implementados Agregar los 6 que faltan (prealertado, recibido_sin_identificar, recibido_identificado, pendiente_conciliacion, esperando_instrucciones, reservado_para_consolidacion, devuelto_a_tienda, descartado_o_cerrado)
15 categorías aduaneras (doc) 15 implementadas (app) Coinciden ✅, pero los nombres difieren en algunos casos. Verificar nomenclatura.

Criterios de aceptación:

  • Los 8 estados de prealerta están en PurchaseReportStatus.
  • Los 16 estados de paquete físico están en PhysicalPackageStatus.
  • Las 15 categorías tienen los nombres exactos de la doc.

🔴 T-406: Maquetación del flujo de consolidación (CU-007) — MAQUETACIÓN, sin backend

Por qué: la auditoría 2026-06-07 detectó que ConsolidationListScreen y ConsolidationDetailScreen existen, pero el usuario no puede crear una consolidación propia. Las dos consolidaciones que se ven en el dashboard (CON-001, CON-002) son estáticas de FakeDataSource. No hay:

  • Pantalla de selección múltiple de paquetes.
  • Pantalla de resumen (peso, vía, destinatario, costo).
  • Selector de vía (Barco $2.40 / Avión $3.35).
  • Validación de KYC aprobado.
  • Generación de Quote desde consolidación.
  • Punto de entrada desde ReporteDetalle ("Agregar a consolidación" no hace nada).
  • Botón "Ver envío" desde ReporteDetalle para CONSOLIDADO (handler vacío).

Esto incumple el caso de uso CU-007 completo, que es Alta prioridad MVP.

Alcance MVP visual (sin backend): la consolidación se crea en memoria y se guarda en FakeDataSource.consolidations. No hay lock distribuido, ni cálculo de costo real contra un servidor de la agencia aliada. Las tarifas SHIPPING_RATE_SEA y SHIPPING_RATE_AIR se usan en el cálculo local. El lock y la persistencia real vendrán con el backend.

Archivos a crear / modificar:

  • app/src/main/java/com/example/islabox/ui/screens/reporte/ConsolidationCreateScreen.kt (nuevo, pantalla de selección).
  • app/src/main/java/com/example/islabox/ui/screens/reporte/ConsolidationSummaryScreen.kt (nuevo, resumen + vía + destinatario + costo).
  • x] DesdeReporteDetallecon estadoEN_ALMACENoLISTO_PARA_CONSOLIDAR`, el botón "Agregar a consolidación" abre la pantalla de selección. ✅
  • El usuario puede seleccionar 2+ paquetes y avanzar. ✅ (cualquier paquete seleccionable)
  • La pantalla de resumen muestra peso total, vía seleccionable (Barco/Avión), destinatario y costo calculado. ✅
  • Si el KYC no está aprobado, muestra pantalla de bloqueo con CTA a KYC. ✅ (KycBlockedScreen en ConsolidationCreate)
  • Al confirmar, crea la Consolidation en FakeDataSource, los ConsolidationPackage, una Quote con vigencia 7 días y navega a QuoteScreen con el id real. ❌ Navega a CON-NEW ficticio (T-417/Consolidation)
  • Desde ConsolidationListScreen hay un FAB "Nueva consolidación" siempre visible. ✅
  • El dashboard no oculta la card "Tus consolidaciones" cuando count == 0. ✅ (ConsolidationsActionCard con CTA "Agrupa tus paquetes y ahorra en el envío")

  • Desde ReporteDetalle con estado EN_ALMACEN o LISTO_PARA_CONSOLIDAR, el botón "Agregar a consolidación" abre la pantalla de selección.

  • El usuario puede seleccionar 2+ paquetes y avanzar.
  • La pantalla de resumen muestra peso total, vía seleccionable (Barco/Avión), destinatario y costo calculado.
  • Si el KYC no está aprobado, muestra pantalla de bloqueo con CTA a KYC.
  • Al confirmar, crea la Consolidation en FakeDataSource, los ConsolidationPackage, una Quote con vigencia 7 días y navega a QuoteScreen con el id real.
  • Desde ConsolidationListScreen hay un FAB "Nueva consolidación" siempre visible (no condicional a count > 0).
  • El dashboard no oculta la card "Tus consolidaciones" cuando count == 0: debe mostrar CTA "Crear primera".

🟡 T-407: Pantalla de notificaciones con datos mockeados (maquetación)

Por qué: existe NotificationsScreen.kt pero solo tiene empty state. La doc define 18+ eventos.

Alcance MVP visual (sin backend): poblar la lista con Notification (data class) y 12 datos de muestra en FakeDataSource.notifications. No se generan por eventos reales.

Cambio: agregar data class Notification y poblar FakeDataSource.notifications con ejemplos de los 18+ eventos definidos en la doc (paquete recibido, en aduana, entregado, etc.). Conectar NotificationsScreen con LazyColumn para mostrar la lista.


🟡 T-408: Integración real con CameraX en KYC (no requiere backend, solo libs nativas)

Por qué: KycCamera simula la captura devolviendo un id timestamp. Falta la integración real.

Acción:

  • Agregar androidx.camera:camera-camera2, camera-lifecycle, camera-view a libs.versions.toml.
  • Crear KycCameraX que use PreviewView + ImageCapture.
  • Reemplazar simulateCapture() por captura real.

Criterios de aceptación:

  • La pantalla de captura muestra preview de cámara en vivo.
  • El botón "Capturar" toma una foto real.
  • La foto se persiste (en memoria por ahora) y se puede ver en preview.

🟡 T-409: Implementar ViewModel + StateFlow en 2-3 pantallas

Por qué: la app no tiene ViewModels. Todo el estado vive en mutableStateOf dentro de Composables. Esto bloquea tests unitarios y rompe con la arquitectura de Android recomendada.

Acción:

  • Agregar androidx.lifecycle:lifecycle-viewmodel-compose a libs.versions.toml.
  • Crear DashboardViewModel, ReportesViewModel, KycViewModel.
  • Refactorizar las 3 pantallas para consumir el VM.
  • Patrón de ejemplo: StateFlow<UiState> con sealed class UiState { Loading, Success, Error }.

Criterios de aceptación:

  • Las 3 pantallas usan viewModel() para su estado.
  • El estado sobrevive a cambios de configuración (rotación).
  • Hay un test unitario del ViewModel.

🟡 T-410: Tests unitarios para LockerCodeGenerator

Por qué: el algoritmo Mod-10 está implementado pero no validado por tests.

Archivo a crear: app/src/test/java/com/example/islabox/core/util/LockerCodeGeneratorTest.kt

Casos de prueba:

  • calculateCheckDigit(1042) == 8
  • format(1042) == "CP-001042-8"
  • validate("CP-001042-8") == Valid
  • validate("CP-001042-7") == InvalidCheckDigit
  • validate("cp0010428") == Valid (normalización)
  • validate("CP-001042") == Invalid (longitud)
  • validate("XX-001042-8") == InvalidPrefix

Criterios de aceptación:

  • Los 7 casos pasan.
  • La cobertura de LockerCodeGenerator es 100%.

🟡 T-411: Limpieza de archivos huérfanos

Por qué: T-204 y T-205 quedaron como "resolución parcial" porque la doc no confirmó la eliminación física.

Verificar y eliminar:

  • app/src/main/java/com/example/islabox/ui/screens/main/ReporteScreen.kt
  • app/src/main/java/com/example/islabox/domain/model/SampleModel.kt
  • app/src/main/java/com/example/islabox/domain/repository/SampleRepository.kt (si existe)

Criterios de aceptación:

  • git status no muestra los archivos.
  • El proyecto compila.
  • No hay imports rotos.

🟢 T-412: Cargar la fuente Inter desde res/font/

Por qué: Type.kt define FontFamily.SansSerif como placeholder. La doc pide Inter.

Archivos a crear:

  • app/src/main/res/font/inter_regular.ttf
  • app/src/main/res/font/inter_semibold.ttf
  • app/src/main/res/font/inter_bold.ttf
  • app/src/main/res/font/inter_extrabold.ttf

Criterios de aceptación:

  • La app usa Inter en todas las pantallas.

--Estado al 2026-06-07: ✅ Resuelto y obsoleta. AndroidManifest.xml no declara package= (es obsoleto desde AGP 7+); el namespace = "com.islabox.app" del build.gradle.kts define el R class y el applicationId. Los import com.islabox.app.R en los archivos .kt funcionan correctamente. La inconsistencia reportada ya no existe. Tarea eliminada del backlog.**

Por qué (motivo original): app/build.gradle.kts define namespace = "com.islabox.app" pero AndroidManifest.xml declara <activity android:name="com.example.islabox.MainActivity">. Esto causa warnings en build y bloquea el correcto funcionamiento de com.islabox.app.R que ya usan los archivos (visto en RegisterScreen.kt línea 17: import com.islabox.app.R).

Archivos a modificar:

  • app/src/main/AndroidManifest.xml — cambiar android:name de la activity.

Cambio:

<activity
    android:name=".MainActivity"
    ...>

Criterios de aceptación:

  • Build sin warnings de package. ✅
  • com.islabox.app.R resuelve en todos los archivos. ✅ android:name=".MainActivity" ...> ``` Estado al 2026-06-07: ✅ Resuelto**.
  • User.kt tiene countryCode: String = "US" (no editable, ver constructor).
  • CurrentUser.kt establece countryCode = "US".
  • RegisterScreen.kt tiene availableCountries = listOf(countries[0]) (bloqueado a +1) y phoneIsValid = phone.length == 10 && isUS.
  • Mensaje de error R.string.origin_rule_error: "IslaBox está disponible solo para usuarios en EE.UU. Si quieres recibir un paquete, pídele a quien te envía que se registre."

Por qué: la Regla de origen del usuario establece que solo se registran personas en EE.UU. La app actualmente permite el país con un dropdown pero la implementación debe:

  • (a) En el registro: el campo phone debe ser +1 (EE.UU., 10 dígitos). El dropdown puede seguir existiendo pero si selecciona otro país, mostrar error.
  • (b) En el modelo User: agregar countryCode: String = "US" no editable.
  • (c) Mensaje de error UX: "IslaBox está disponible solo para usuarios en EE.UU. Si quieres recibir un paquete, pídele a quien te envía que se registre."

Archivos a modificar:

  • app/src/main/java/com/example/islabox/domain/model/User.kt — agregar countryCode.
  • app/src/main/java/com/example/islabox/ui/screens/auth/RegisterScreen.kt — validar.
  • app/src/main/java/com/example/islabox/data/local/CurrentUser.kt — agregar countryCode = "US".

Criterios de aceptación:

  • User.countryCode es US y no es editable. ✅
  • El registro rechaza teléfonos que no sean +1 con 10 dígitos. ✅
  • El mensaje de error es claro y amable. ✅

  • app/src/main/java/com/example/islabox/domain/model/User.kt — agregar countryCode.

  • app/src/main/java/com/example/islabox/ui/screens/auth/RegisterScreen.kt — validar.
  • app/src/main/java/com/example/islabox/data/local/CurrentUser.kt — agregar countryCode = "US".

Criterios de aceptación:

  • User.countryCode es US y no es editable.
  • El registro rechaza teléfonos que no sean +1 con 10 dígitos.
  • El mensaje de error es claro y amable.

🟠 T-415: Componente IslaPhoneFieldInternational para destinatarios

Por qué: el campo phone de Contact/Recipient debe aceptar +53 (Cuba, 8 dígitos) además de +1 (EE.UU., 10 dígitos). El IslaPhoneField actual solo soporta +1.

Archivos a crear / modificar:

  • app/src/main/java/com/example/islabox/ui/components/common/IslaTextFields.kt — refactorizar IslaPhoneField o crear variante IslaPhoneFieldInternational parametrizable por recipientCountries: List<Country>.

Criterios de aceptación:

  • El campo phone de Contact/Recipient acepta +53 y +1.
  • El placeholder y maxLength se adaptan al país seleccionado.
  • Al guardar, se almacena en formato E.164 (+53XXXXXXXX o +1XXXXXXXXXX).

🟠 T-416: DestinatarioSelector debe permitir alta y edición

Por qué: el componente muestra un card con el destinatario pero no permite agregar uno nuevo ni editar los existentes. El usuario queda sin forma de gestionar sus destinatarios en Cuba.

Archivos a modificar:

  • app/src/main/java/com/example/islabox/ui/components/reporte/DestinatarioSelector.kt
  • Crear app/src/main/java/com/example/islabox/ui/screens/main/ContactosScreen.kt (gestión completa).

Criterios de aceptación:

  • Desde el formulario de reporte, el usuario puede tocar "+ Agregar destinatario".
  • Se abre un bottom sheet o pantalla de alta con nombre, CI, dirección, provincia, municipio, teléfono.
  • El nuevo destinatario se guarda en FakeDataSource.recipients y queda seleccionable. Estado al 2026-06-07: ✅ Resuelto**. AttentionSection muestra los 7 AttentionType con navegación contextual. FakeDataSource.attentionItems tiene los datos de muestra.

Por qué: la doc funcional define 7 tipos de elementos que requieren acción: KYC rechazado, paquete sin prealerta, almacenaje por vencer, pago pendiente, aduana requiere información, servicio completado, producto restringido. La app no tiene esta sección.

Criterios de aceptación:

  • El dashboard muestra esta sección solo cuando hay elementos. ✅
  • Cada item tiene CTA directo a la pantalla relevante (KYC, Reportes, Cotización, etc.). ✅l guardar un reporte, no se persiste el senderId. Falta la conexión con el purchase_report_id del modelo Reporte.remitenteId.

Criterios de aceptación:

  • Al guardar un reporte, Reporte.remitenteId queda con el id del usuario actual.
  • Si hay más remitentes guardados (Fase 2), se muestran como opción.

🟠 T-418: Sección "Requiere tu atención" en Dashboard

Por qué: la doc funcional define 7 tipos de elementos que requieren acción: KYC rechazado, paquete sin prealerta, almacenaje por vencer, pago pendiente, aduana requiere información, servicio completado, producto restringido. La app no tiene esta sección. Estado al 2026-06-07: ⚠️ Pantalla creada, link de acceso pendiente. AduanaInfoScreen.kt está implementada con 6 secciones y fecha de norma 2026-06-04. La ruta Screen.AduanaInfo existe en NavGraph.kt pero ningún botón de la app navega hacia ella**. Falta agregar el link en GuiaScreen (T-419-link en estado-actual.md).

Por qué: la doc pide que el usuario pueda ver reglas de aduana, límites, exenciones y glosario. No existe.

Acción:

  • Crear app/src/main/java/com/example/islabox/ui/screens/guia/AduanaInfoScreen.kt.
  • Mostrar las 6 secciones definidas en datos/aduana.md.

Criterios de aceptación:

  • Pantalla creada. ✅
  • Muestra fecha de actualización de la norma. ✅
  • El usuario accede desde la sección "Guía" del dashboard. ❌ (T-419-link) reglas de aduana, límites, exenciones y glosario. No existe.

Acción:

  • Crear app/src/main/java/com/example/islabox/ui/screens/guia/AduanaInfoScreen.kt.
  • Mostrar las 6 secciones definidas en datos/aduana.md.

Criterios de aceptación:

  • El usuario accede desde la sección "Guía" del dashboard.
  • Muestra fecha de actualización de la norma.

🟡 T-420: Drawable de los logos en formato vectorial

Por qué: T-001 quedó con PNG. Aunque funcional, los PNG pesan más y no escalan bien. Migrar a VectorDrawable es preferible.

Acción: convertir los 4 PNG a XML vectoriales y actualizar IslaImages.kt.


🟢 T-421: Eliminar colores hardcodeados en componentes

Por qué: la doc tema-diseno.md señala 3 grises diferentes hardcodeados (0xFFF1F4F9, 0xFFF0F4F8, 0xFFF0F0F5) y 2 rojos (0xFFBB0016 paleta, 0xFFD32F2F Cerrar Sesión). Unificar a tokens del theme.

Archivos a revisar: cualquier .kt con literales Color(0xFF...).


🆕 Tareas nuevas — auditoría de flujos 2026-06-07

Alcance del Bloque D: el objetivo es avanzar en la maquetación visual de la APK sin backend ni autenticación real. Las validaciones que se implementen son solo de cliente (regex, longitud, formato) para mejorar la UX; ninguna tarea de este bloque pretende reemplazar lógica de servidor (login real, cobro real, conciliación real, etc.). Donde un flujo completo dependa de backend, la tarea se limita a dejar la UI navegable y mostrar datos mockeados de FakeDataSource.

Estas tareas surgieron del análisis del código y de los flujos de cara al usuario final. Ver detalle completo en informe_flujo.md.

🔴 T-422: ReporteDetalle debe enlazar con QuoteScreen usando datos mockeados coherentes

Por qué: ReporteDetalleScreen.kt:360 usa onPayClick = { onPayClick("QUO-001") } con un id hardcodeado. Independientemente del paquete, siempre abre QuoteScreen con la cotización de muestra. La UI debe enlazar a una Quote específica del FakeDataSource que sea coherente con el Reporte que el usuario está viendo.

Archivos a modificar:

  • app/src/main/java/com/example/islabox/data/local/FakeDataSource.kt — agregar helper findQuoteForReporte(reporteId: String): Quote? que devuelva la cotización asociada al reporte o null si no hay.
  • app/src/main/java/com/example/islabox/ui/screens/reporte/ReporteDetalleScreen.kt — usar el helper en onPayClick para pasar el id real.
  • Agregar cotizaciones de muestra en FakeDataSource.quotes para al menos los reportes con packageStatus = LISTO_PARA_ENVIAR o CONSOLIDADO.

Alcance MVP visual (sin backend): no se pretende que el pago funcione, solo que la navegación llegue a una pantalla de cotización con datos coherentes.

Criterios de aceptación:

  • Cada reporte con packageStatus = LISTO_PARA_ENVIAR o CONSOLIDADO muestra su Quote específica al tocar "Pagar Envío".
  • Si el reporte no tiene cotización, el botón muestra un toast "No hay cotización disponible" en vez de navegar.
  • QuoteScreen muestra "Cotización no encontrada" si el id no existe en FakeDataSource.quotes.

🔴 T-423: PerfilScreen debe tener navegación a sub-pantallas (maquetación, sin backend)

Por qué: 7 de 8 items del menú de PerfilScreen tienen onClick vacío o /* TODO */. Items afectados: "Mi Información", "Direcciones", "Métodos de Pago", "Notificaciones", "Ayuda y Soporte", "Privacidad", y el botón "Cerrar Sesión". El usuario ve un menú profesional pero los items no navegan a ningún lado.

Archivos a modificar / crear:

  • app/src/main/java/com/example/islabox/ui/screens/main/PerfilScreen.kt — cablear onClick en los items para que naveguen a pantallas (existentes o nuevas) en lugar de quedar vacíos.
  • Reusar ContactosScreen (ya existe) para "Direcciones".
  • Crear EditProfileScreen.kt (UI con campos y validaciones de cliente, sin llamada a backend).
  • Crear PaymentMethodsScreen.kt (UI con lista de tarjetas hardcodeadas en FakeDataSource, sin integrar Stripe).
  • "Cerrar Sesión" debe navegar a Screen.Welcome (sin limpiar tokens, porque todavía no hay sesión real).
  • Items sin pantalla: mostrar un toast "Próximamente" o ComingSoonScreen placeholder.

Alcance MVP visual (sin backend): las pantallas se ven completas, navegan correctamente, muestran datos de FakeDataSource, pero ninguna llama a un endpoint.

Criterios de aceptación:

  • "Mi Información" navega a EditProfileScreen con formulario editable.
  • "Cerrar Sesión" navega a Screen.Welcome (sin lógica de tokens).
  • "Direcciones" navega a ContactosScreen.
  • "Métodos de Pago" navega a PaymentMethodsScreen con lista mockeada.
  • Los items restantes ("Notificaciones", "Ayuda y Soporte", "Privacidad") muestran un toast "Próximamente" o navegan a ComingSoonScreen.

🟠 T-424: ReportActionBanner debe llevar a ReporteForm, no a Reportes (maquetación)

Por qué: DashboardScreen.kt:199 hace que el banner "Reportar compra" navegue a la lista Reportes en vez de al formulario. El usuario tiene que tocar el banner, ver la lista, y volver a tocar el FAB. Doble fricción.

Archivos a modificar:

  • app/src/main/java/com/example/islabox/ui/screens/main/DashboardScreen.kt — cambiar onClick a navController.navigate(Screen.Dashboard.ReporteForm).

Alcance MVP visual (sin backend): solo se ajusta la navegación. El handler de envío del formulario sigue mockeado (T-201).

Criterios de aceptación:

  • El banner "Reportar compra" del dashboard abre directamente el formulario.

🟠 T-425: ReporteDetalle debe mostrar nombre del destinatario (no el ID)

Por qué: ReporteDetalleScreen.kt:234 muestra reporte.destinatarioId ?: "No asignado". El usuario ve CONT-001 en vez de "Juan Pérez". La doc pide ver el nombre del destinatario.

Archivos a modificar:

  • app/src/main/java/com/example/islabox/ui/screens/reporte/ReporteDetalleScreen.kt — hacer join con FakeDataSource.recipients (o el repository) y mostrar Contact.name.

Alcance MVP visual (sin backend): solo UI. El join se hace contra la lista en memoria de FakeDataSource.recipients.

Criterios de aceptación:

  • El destinatario se muestra con su nombre completo (Contact.name) en vez del id (CONT-001).
  • Si el destinatario no existe en FakeDataSource.recipients, se muestra "Destinatario no encontrado" en vez del id crudo.

🔴 T-426: LoginScreen debe validar formato en cliente (sin backend)

Por qué: LoginScreen.kt:90-91 navega a Main con cualquier toque del botón, incluso con campos vacíos. Aunque no hay backend para validar credenciales reales, se debe mejorar la UX: deshabilitar el botón mientras el email no tenga formato válido o la contraseña esté vacía.

Archivos a modificar:

  • app/src/main/java/com/example/islabox/ui/screens/auth/LoginScreen.kt — agregar validación inline (email regex, password no vacía) y deshabilitar botón si inválido. Mostrar errores inline.

Alcance MVP visual (sin backend): la validación es solo de formato en cliente. No se pretende validar credenciales reales. El botón navega a Main cuando ambos campos cumplen formato, simulando el login exitoso (mismo comportamiento actual).

Criterios de aceptación:

  • El botón "Entrar" se deshabilita si el email no tiene formato válido o la contraseña está vacía.
  • Errores inline visibles en tiempo real (sin esperar al tap).
  • El flujo visual de "login -> dashboard" sigue funcionando para demos (sin crash).

🟠 T-427: RegisterScreen debe validar email y requisitos de contraseña (sin backend)

Por qué: RegisterScreen valida origen US y coincidencia de contraseñas, pero no valida formato de email ni requisitos de contraseña (longitud, dígitos, mayúsculas). El botón se habilita con isFormValid = firstName.isNotEmpty() && ... && passwordsMatch.

Archivos a modificar:

  • app/src/main/java/com/example/islabox/ui/screens/auth/RegisterScreen.kt — agregar validateEmail(email) (regex) y validatePassword(password) (mín. 8 chars, 1 mayúscula, 1 dígito) con errores inline. Agregar checkbox de términos obligatorio.

Alcance MVP visual (sin backend): la validación es solo de formato en cliente. No se pretende crear el usuario en un servidor. El handler onRegisterClick se mantiene navegando a Main (mock).

Criterios de aceptación:

  • Email inválido muestra error inline y deshabilita el botón.
  • Contraseña con menos de 8 caracteres muestra errodeben navegar a pantallas (sin backend)

Por qué: ReporteDetalleScreen.kt:282 tiene // TODO: Otras acciones para "Solicitar reempaque", "Agregar a consolidación" y "Enviar solo". Los 3 botones se muestran pero no hacen nada.

Archivos a modificar:

  • app/src/main/java/com/example/islabox/ui/screens/reporte/ReporteDetalleScreen.kt — agregar onRequestRepack y onAddToConsolidation como parámetros y conectarlos en NavGraph.
  • app/src/main/java/com/example/islabox/ui/navigation/NavGraph.kt — registrar ruta para RequestRepack (similar a RequestInspection).
  • Conectar "Enviar solo" con navegación a QuoteScreen usando el helper de T-422.

Alcance MVP visual (sin backend): los botones navegan a sus pantallas, pero las pantallas agregan a FakeDataSource (no llaman a endpoints).

Criterios de aceptación:

  • "Solicitar reempaque" navega a la pantalla de solicitud de reempaque.
  • "Agregar a consolidación" abre ConsolidationCreateScreen con el paquete preseleccionado.
  • "Enviar solo" navega a QuoteScreen con la cotización del paquete (helper de T-422)

  • app/src/main/java/com/example/islabox/ui/screens/reporte/ReporteDetalleScreen.kt — agregar onRequestRepack y onAddToConsolidation como parámetros y conectarlos en NavGraph.

  • app/src/main/java/com/example/islabox/ui/navigation/NavGraph.kt — registrar ruta para RequestRepack (similar a RequestInspection).
  • Conectar "Enviar solo" con la creación de un Shipment individual (sin consolidación) y navegación a QuoteScreen.

Criterios de aceptación:

  • "Solicitar reempaque" navega a la pantalla de solicitud de reempaque.
  • "Agregar a consolidación" abre ConsolidationCreateScreen con el paquete preseleccionado.
  • "Enviar solo" genera una cotización individual y navega a QuoteScreen.

🟡 T-429: Notificaciones demo con banner "Demo" en MVP

Por qué: FakeDataSource.notifications tiene 12 notificaciones estáticas (subiste de nivel, crédito, etc.) que la NotificationsScreen muestra como si fueran del usuario real. Riesgo de confusión en demo con stakeholders.

Archivos a modificar:

  • app/src/main/java/com/example/islabox/ui/screens/main/NotificationsScreen.kt — agregar un banner superior "Datos de demostración" mientras no haya backend.

Alcance MVP visual (sin backend): solo UI. Las notificaciones son estáticas, no se generan por eventos.

Criterios de aceptación:

  • La pantalla de notificaciones muestra un banner amarillo "Estás viendo datos de demostración" en MVP.
  • El banner desaparece cuando se conecte el backend real.

Resumen de avance — actualizado al 2026-06-07 (verificado contra código)

Sprint / bloque Tareas ✅ Completadas ❌ Abiertas % Avance
0 — Crash fixes 3 3 0 100%
1 — Datos y FakeDataSource 6 6 0 100%
2 — Pantallas Auth/KYC 6 6 0 100%
3 — Componentes y validaciones 5 5 0 100%
4 — Envíos y paquetes 2 2 0 100%
5 — Servicios 3 3 0 100%
Tareas transversales (ProGuard, Java 17, T-413 obsoleta, T-414) 4 4 0 100%
Bloque B — Vigentes (T-417, T-501-persist) 2 0 2 0%
Bloque C — Nuevas 2026-06-04 (T-419, T-423, T-425, T-206b, T-405, T-427, T-428, T-429, T-431) 9 5 4 56%
Bloque D — Maquetación visual sin backend (T-422-persist, T-422-onPayConsolidation, T-433, T-434, etc.) 8 4 4 50%
Total 48 37 (77%) 10 (21%) 77%

Nota: el conteo se redujo de 46 a 48 porque la auditoría del 2026-06-07 agregó T-431 (captura de ZIP en KYC) y T-501-persist (persistencia del nuevo ServiceRequest). T-413 se marca como obsoleta. T-414 se movió a "transversales" ya resuelta.

Veredicto: la APK está 77% completa sobre el plan original. El 21% restante son, en su mayoría, tareas de persistencia de formularios (los handlers de submit no escriben a FakeDataSource), validaciones de cliente (email, password) y navegación de Perfil (7/8 items sin onClick). No son bloqueantes para una demo de maquetación visual; son necesarias para que la app sea coherente de extremo a extremo.

Tareas críticas pendientes (orden de prioridad):

  1. T-417: persistir nuevo Reporte en FakeDataSource.
  2. T-501-persist: persistir nuevo ServiceRequest.
  3. T-405: alinear enums con nombres oficiales snake_case en español.
  4. T-206b: actualizar AppSettings a los valores de la doc (15 días, $0.50, 3 días).
  5. T-423: navegación de sub-pantallas de PerfilScreen (7/8 items).
  6. T-425: mostrar nombre del destinatario en ReporteDetalle en vez del ID.
  7. T-419-link: link a AduanaInfo desde la nav.
  8. T-427/T-428: validaciones de email y password en RegisterScreen.
  9. T-429: banner "Demo" en NotificationsScreen.
  10. T-431: captura de ZIP en KycResidenceScreen.