FTL TRACE TX — Mobile Handoff

Para: Andrés Linares  ·  De: Franco Cedillo  ·  2026-06-17  ·  Android Java · Retrofit 2.9.0 · Gson
D1 · CP7 Verificar D2 · CP7 Completar D3 · CP8 Verificar (bloqueado) Backend ya en develop
D1
CP7 Verificar tabla colores
D2
CP7 Completar toggle + alerta
D3
CP8 Verificar (post-B1)
ℹ️
Flujo genérico — ninguna screen nueva CP7 y CP8 usan el flujo genérico: CheckpointFragment → VerificarFragment → CompletarFragment. Solo CP6 tiene screens dedicadas. Los cambios son extensiones condicionales de los fragments existentes.
D1

CP7 Verificar — Tabla de inspección por color

Listo para implementar
Backend ya desplegado en develop El endpoint precargado CP7 ahora devuelve inspecciones_por_color, total_primeras_efectivas, porcentaje_primeras y más. Solo hay que leerlos y mostrarlos.

Endpoint

GET /api/v1/checkpoints/7/lote/{lote_id}/precargado

Response — campos nuevos (añadir a PrecargadoResponse.java)

{
  "checkpoint": 7,
  "lote_id": "uuid...",
  "datos_precargados": [ /* campos existentes sin cambios */ ],

  // ──── NUEVOS CAMPOS ────────────────────────────────────
  "inspecciones_por_color": {          // Map<String, Integer>
    "BLACK":        299,              // primeras_efectivas
    "FUCHSIA":      119,
    "HARBOR":       313,
    "HONEY":        166,
    "LENTIL":       139,
    "SIENNA":       299,
    "TEAL":         141,
    "WINTER WHITE": 338
  },
  "total_pedido":             1884,   // Integer
  "total_primeras_efectivas": 1814,   // Integer
  "total_segundas":           31,     // Integer
  "porcentaje_primeras":      96.3    // Double — usar < 95.0 para alerta
}
Actualizar el modelo Gson — PrecargadoResponse.java

Añadir los 5 campos nuevos. Gson los ignora automáticamente si no existen en el JSON (null), así que es backwards-compatible.

// En PrecargadoResponse.java — agregar junto a los campos existentes
@SerializedName("inspecciones_por_color")
public Map<String, Integer> inspeccionesPorColor;

@SerializedName("total_pedido")
public Integer totalPedido;

@SerializedName("total_primeras_efectivas")
public Integer totalPrimerасEfectivas;

@SerializedName("total_segundas")
public Integer totalSegundas;

@SerializedName("porcentaje_primeras")
public Double porcentajePrimeras;
VerificarFragment.java — mostrar tabla cuando cp_num == 7

Después de renderizar datos_precargados, añadir bloque condicional:

if (cpNum == 7 && response.inspeccionesPorColor != null
        && !response.inspeccionesPorColor.isEmpty()) {

    // Mostrar encabezado de sección
    TextView header = new TextView(getContext());
    header.setText("Inspección por color");
    header.setTextAppearance(R.style.TextAppearance_MaterialComponents_Subtitle2);
    containerLayout.addView(header);

    // Una fila por color — usar LinearLayout o RecyclerView
    for (Map.Entry<String, Integer> entry :
            response.inspeccionesPorColor.entrySet()) {
        View row = buildColorRow(entry.getKey(), entry.getValue());
        containerLayout.addView(row);
    }

    // Fila de totales
    View totalRow = buildColorRow("TOTAL", response.totalPrimerасEfectivas);
    containerLayout.addView(totalRow);

    // Badge de alerta si porcentaje < 95%
    if (response.porcentajePrimeras != null
            && response.porcentajePrimeras < 95.0) {
        Chip chip = new Chip(getContext());
        chip.setText("⚠ " + response.porcentajePrimeras + "%");
        chip.setChipBackgroundColorResource(R.color.red_100);
        chip.setTextColor(getResources().getColor(R.color.red_800));
        containerLayout.addView(chip);
    }
}

UX de la tabla

ColorPrimeras efectivas
BLACK299
FUCHSIA119
HARBOR313
HONEY166
LENTIL139
SIENNA299
TEAL141
WINTER WHITE338
TOTAL1,814
D2

CP7 Completar — Toggle Aprobado/Rechazado + alerta desglose

Listo para implementar
El backend auto-deriva prendas — el operario ya no las ingresa prendas_primera, prendas_segunda, etc. se calculan automáticamente desde cp7_inspecciones. El operario solo necesita seleccionar Aprobado/Rechazado y opcionalmente ingresar la energía (fallback IoT).

Cambios en CheckpointConfig.java — CP7

CampoAcciónMotivo
prendas_primeraELIMINARAuto-derivado por backend
prendas_segundaELIMINARAuto-derivado por backend
prendas_saldoELIMINARAuto-derivado por backend
prendas_descarteELIMINARAuto-derivado por backend
prendas_desmancheELIMINARAuto-derivado por backend
prendas_composturaELIMINARAuto-derivado por backend
prendas_zurcidoELIMINARAuto-derivado por backend
resultado_calidadAGREGAR tipo dropdownNuevo — Aprobado/Rechazado
energia_inspeccion_kwhMANTENERFallback si IoT no disponible

Endpoint completar — payload

POST /api/v1/checkpoints/7/lote/{lote_id}/completar

// Body (datos_completados map)
{
  "resultado_calidad": "aprobado",    // o "rechazado"
  "energia_inspeccion_kwh": 2.5        // opcional — solo si IoT no disponible
  // prendas_* ya NO se envían — el backend los auto-calcula
}

Response — leer el desglose

{
  "exitoso": true,
  "desglose": {
    "primeras":            1814,
    "segundas":            31,
    "saldo":               0,
    "descarte":            0,
    "total":               1845,   // de costura CP6
    "neto_apto":           1814,
    "diferencia":          0,       // suma - total (puede ser negativo)
    "porcentaje_variacion": 0.0,
    "alerta":              false   // true si diferencia != 0
  }
}
CompletarFragment.java — toggle buttons para resultado_calidad

Cuando cpNum == 7, reemplazar el dropdown genérico de resultado_calidad con dos botones toggle:

if (cpNum == 7 && field.campo.equals("resultado_calidad")) {
    // Usar MaterialButtonToggleGroup o dos Buttons
    MaterialButtonToggleGroup group = new MaterialButtonToggleGroup(ctx);
    group.setSingleSelection(true);
    group.setSelectionRequired(true);

    MaterialButton btnAprobado = new MaterialButton(ctx,
        null, R.attr.materialButtonOutlinedStyle);
    btnAprobado.setText("✓  Aprobado");
    btnAprobado.setTag("aprobado");

    MaterialButton btnRechazado = new MaterialButton(ctx,
        null, R.attr.materialButtonOutlinedStyle);
    btnRechazado.setText("✗  Rechazado");
    btnRechazado.setTag("rechazado");

    group.addView(btnAprobado);
    group.addView(btnRechazado);

    // Al submit, leer valor seleccionado
    datosCompletados.put("resultado_calidad", getSelectedTag(group));
}
CompletarFragment.java — mostrar Snackbar si desglose.alerta == true

En el callback de la respuesta de completar:

if (response.desglose != null && Boolean.TRUE.equals(response.desglose.alerta)) {
    String signo = response.desglose.diferencia >= 0 ? "+" : "";
    String msg = "Diferencia vs CP6: " + signo
        + response.desglose.diferencia + " prendas ("
        + response.desglose.porcentajeVariacion + "%). Puede continuar.";
    Snackbar.make(requireView(), msg, Snackbar.LENGTH_LONG)
        .setBackgroundTint(getResources().getColor(R.color.amber_700))
        .show();
    // NO bloquear — el operario puede continuar al siguiente paso
}
Modelo Gson para desglose — añadir clase o inner class
public class Cp7Desglose {
    public Integer primeras;
    public Integer segundas;
    public Integer saldo;
    public Integer descarte;
    public Integer total;
    @SerializedName("neto_apto")    public Integer netoApto;
    public Integer diferencia;
    @SerializedName("porcentaje_variacion") public Double porcentajeVariacion;
    public Boolean alerta;
}

// En CompletarResponse.java (o el response existente de completar):
public Cp7Desglose desglose;
D3

CP8 Verificar — Tabla combinada inspección + packing

Bloqueado — post-deploy B1
⚠ Bloqueado El endpoint CP8 verificar ya está actualizado en develop pero debe desplegarse y validarse antes de implementar en mobile. Implementar D3 solo cuando B1 esté en producción.

Endpoint — diferente al precargado genérico

GET /api/v1/checkpoints/cp8/lote/{lote_id}/verificar?session_id={session_id}
// NOTA: este endpoint NO es el precargado genérico — es el verificar específico de CP8

Response — nuevos campos

{
  "session_id": "uuid...",
  "orden_produccion": "OP-6989",
  // ... campos existentes ...

  // ──── NUEVOS — tabla combinada ────────────────────────
  "colores_cp8": [
    { "color": "BLACK",        "primeras_efectivas": 299, "qty_pedido": 311, "diferencia": 12  },
    { "color": "FUCHSIA",      "primeras_efectivas": 119, "qty_pedido": 128, "diferencia": 9   },
    { "color": "HARBOR",       "primeras_efectivas": 313, "qty_pedido": 309, "diferencia": -4  },
    { "color": "HONEY",        "primeras_efectivas": 166, "qty_pedido": 171, "diferencia": 5   },
    { "color": "LENTIL",       "primeras_efectivas": 139, "qty_pedido": 150, "diferencia": 11  },
    { "color": "SIENNA",       "primeras_efectivas": 299, "qty_pedido": 292, "diferencia": -7  },
    { "color": "TEAL",         "primeras_efectivas": 141, "qty_pedido": 150, "diferencia": 9   },
    { "color": "WINTER WHITE", "primeras_efectivas": 338, "qty_pedido": 373, "diferencia": 35  }
  ],
  "total_pedido":             1884,
  "total_primeras_efectivas": 1814
}

UX requerida

ColorPrimeras EfectivasPedidoDiferencia
BLACK299311+12
HARBOR313309−4
TOTAL1,8141,884+70

Diferencias negativas en rojo. Diferencias positivas en verde.

📁

Archivos a modificar

ArchivoCambioTarea
network/model/PrecargadoResponse.java Añadir 5 campos nuevos con @SerializedName D1
fragment/VerificarFragment.java Bloque if (cpNum == 7) → tabla colores + badge % D1
checkpoint/CheckpointConfig.java CP7: eliminar campos prendas_*, agregar resultado_calidad D2
fragment/CompletarFragment.java CP7: toggle buttons + Snackbar desglose.alerta D2
network/model/Cp7Desglose.java (crear) Clase Gson para el desglose de CP7 completar D2
network/model/Cp8VerificarResponse.java (crear o extender) Campos colores_cp8, total_pedido, etc. D3 — post B1
fragment/VerificarFragment.java Bloque if (cpNum == 8) → tabla 4 columnas D3 — post B1
🧪

Datos de prueba — OP-6989 Hábitat

GS1-128 CP7 checkin
(10)OP-6989(240)T001-22621
Backend dev
https://dev-back.dpp.fintechlab.la
OP / Cliente
OP-6989 · Hábitat · Estilo 27553
Primeras efectivas totales
1,814 / 1,884 pedido (96.3%)
// CP7 Completar — payload mínimo para OP-6989
POST /api/v1/checkpoints/7/lote/{lote_id}/completar
{
  "datos_completados": {
    "resultado_calidad": "aprobado"
    // prendas_* omitidos — backend los auto-calcula desde cp7_inspecciones
  }
}

// Response esperado
{
  "exitoso": true,
  "desglose": {
    "primeras": 1814,
    "diferencia": 0,
    "porcentaje_variacion": 0.0,
    "alerta": false
  }
}