feat(nakui): Fase 2 del ERP — relaciones legibles + formato

Column.ref_entity resuelve un UUID al label del record referido;
Column.format (ValueFormat Number/Currency) agrupa miles y prefija
símbolo. El campo entity_ref en formularios muestra el record elegido
por su label, no el UUID. human_label_for_record reconoce nombre/titulo
(español). El módulo CRM: las listas muestran el nombre del cliente y
monto como $12,000.

Helper format_value en meta-runtime. Tests en meta-schema, meta-runtime
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:05:04 +00:00
parent 86d06da020
commit eba629a806
10 changed files with 277 additions and 36 deletions
@@ -148,12 +148,33 @@ pub struct Column {
/// Ancho relativo (peso flex). Default 1.
#[serde(default = "default_weight")]
pub weight: f32,
/// Si está set, la celda resuelve su valor (un UUID) al label
/// legible del record de esta entity, en vez de mostrar el UUID
/// crudo. Para columnas que son referencias a otra entity.
#[serde(default)]
pub ref_entity: Option<String>,
/// Formato de presentación del valor de la celda.
#[serde(default)]
pub format: ValueFormat,
}
fn default_weight() -> f32 {
1.0
}
/// Formato de presentación de un valor en una celda de lista.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ValueFormat {
/// Sin formato — el valor se muestra crudo. Default.
#[default]
Plain,
/// Entero/decimal con separador de miles (`12000` → `12,000`).
Number,
/// Moneda: separador de miles + símbolo prefijo (`12000` → `$12,000`).
Currency { symbol: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FormView {
pub title: String,
@@ -501,11 +522,15 @@ mod tests {
field: "name".into(),
label: "Nombre".into(),
weight: 2.0,
ref_entity: None,
format: ValueFormat::Plain,
},
Column {
field: "email".into(),
label: "Email".into(),
weight: 3.0,
ref_entity: None,
format: ValueFormat::Plain,
},
],
actions: vec![Action::OpenView {