feat(nakui): Fase 3 del ERP — ficha de detalle

View::Detail: ficha de un record con sus campos + listas de records
relacionados (RelatedList, back-references por via_field) + botones
Volver/Editar. ListView.row_detail enlaza lista→ficha con un botón 👁
por fila; Module::validate exige que apunte a una vista detail. En
meta-form: render_detail/render_related + select_detail con retorno.

El CRM: 👁 en Clientes y Oportunidades abre su ficha; la del cliente
lista sus oportunidades e interacciones. Tests en meta-schema y
nakui-ui verdes; clippy limpio.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-21 19:12:26 +00:00
parent eba629a806
commit 6588d0ed6c
9 changed files with 449 additions and 29 deletions
+26
View File
@@ -430,6 +430,8 @@ mod tests {
"mover_form",
"interaccion_list",
"interaccion_form",
"cliente_detail",
"oportunidad_detail",
] {
assert!(m.views.contains_key(view), "falta la vista «{view}»");
}
@@ -457,6 +459,30 @@ mod tests {
),
"monto debe formatearse como moneda",
);
assert_eq!(
lv.row_detail.as_deref(),
Some("oportunidad_detail"),
"la fila de oportunidad debe abrir su ficha",
);
// Fase 3: la ficha del cliente lista sus oportunidades e
// interacciones (back-references).
let nahual_meta_schema::View::Detail(dv) = &m.views["cliente_detail"] else {
panic!("cliente_detail debe ser una ficha (detail)");
};
assert_eq!(dv.entity, "Cliente");
let related: Vec<&str> = dv.related.iter().map(|r| r.entity.as_str()).collect();
assert!(
related.contains(&"Oportunidad"),
"ficha cliente: falta Oportunidad"
);
assert!(
related.contains(&"Interaccion"),
"ficha cliente: falta Interaccion"
);
for r in &dv.related {
assert_eq!(r.via_field, "cliente_id", "back-ref por cliente_id");
}
}
/// Carga el módulo crm por el mismo camino que usa `nakui-ui`