CheckpointFragment → VerificarFragment → CompletarFragment.
Solo CP6 tiene screens dedicadas. Los cambios son extensiones condicionales de los fragments existentes.
inspecciones_por_color, total_primeras_efectivas, porcentaje_primeras y más. Solo hay que leerlos y mostrarlos.
GET /api/v1/checkpoints/7/lote/{lote_id}/precargado
{
"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
}
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;
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);
}
}
| Color | Primeras efectivas |
|---|---|
| BLACK | 299 |
| FUCHSIA | 119 |
| HARBOR | 313 |
| HONEY | 166 |
| LENTIL | 139 |
| SIENNA | 299 |
| TEAL | 141 |
| WINTER WHITE | 338 |
| TOTAL | 1,814 |
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).
| Campo | Acción | Motivo |
|---|---|---|
prendas_primera | ELIMINAR | Auto-derivado por backend |
prendas_segunda | ELIMINAR | Auto-derivado por backend |
prendas_saldo | ELIMINAR | Auto-derivado por backend |
prendas_descarte | ELIMINAR | Auto-derivado por backend |
prendas_desmanche | ELIMINAR | Auto-derivado por backend |
prendas_compostura | ELIMINAR | Auto-derivado por backend |
prendas_zurcido | ELIMINAR | Auto-derivado por backend |
resultado_calidad | AGREGAR tipo dropdown | Nuevo — Aprobado/Rechazado |
energia_inspeccion_kwh | MANTENER | Fallback si IoT no disponible |
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
}
{
"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
}
}
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));
}
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
}
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;
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
{
"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
}
| Color | Primeras Efectivas | Pedido | Diferencia |
|---|---|---|---|
| BLACK | 299 | 311 | +12 |
| HARBOR | 313 | 309 | −4 |
| TOTAL | 1,814 | 1,884 | +70 |
Diferencias negativas en rojo. Diferencias positivas en verde.
| Archivo | Cambio | Tarea |
|---|---|---|
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 |
// 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
}
}