chore: absorbe nakui (ERP matemático) en modules/nakui

- crates/modules/nakui/core/: el crate nakui-core (4 bins, tests).
  Deps directas (serde, rhai, surrealdb, petgraph, sha2, uuid, tokio,
  thiserror v1) — no convertidas a workspace = true en esta pasada.
- crates/modules/nakui/modules/{inventory,sales,treasury}/: datos
  declarativos del dominio (nsmc.json, schema.k, morphisms/) que el
  crate consume — no son crates.

cargo check -p nakui-core: 0 errores.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-05-08 05:49:58 +00:00
parent 53dbdf0f1d
commit 4d50bfc587
49 changed files with 11953 additions and 40 deletions
+72
View File
@@ -0,0 +1,72 @@
use serde::{Deserialize, Serialize};
use serde_json::Value;
use uuid::Uuid;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FieldPath {
pub entity: String,
pub id: Uuid,
pub field: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "op", rename_all = "snake_case")]
pub enum FieldOp {
Set {
path: FieldPath,
value: Value,
},
Create {
entity: String,
id: Uuid,
data: Value,
},
Delete {
entity: String,
id: Uuid,
},
}
impl FieldOp {
/// Token a manifest's `writes` list matches against.
/// "Caja.saldo" for field updates, "Movimiento" for whole-record ops.
pub fn capability_token(&self) -> String {
match self {
FieldOp::Set { path, .. } => format!("{}.{}", path.entity, path.field),
FieldOp::Create { entity, .. } => entity.clone(),
FieldOp::Delete { entity, .. } => entity.clone(),
}
}
}
/// Apply only the ops that target `(entity, id)` to `state` and return the
/// new value. Returns `None` if a Delete op removes the target — callers
/// should skip post-checks against a deleted entity rather than running
/// them against the stale prior state.
pub fn simulate_on(state: &Value, entity: &str, id: Uuid, ops: &[FieldOp]) -> Option<Value> {
let mut s: Option<Value> = Some(state.clone());
for op in ops {
match op {
FieldOp::Set { path, value } if path.entity == entity && path.id == id => {
if let Some(Value::Object(map)) = s.as_mut() {
map.insert(path.field.clone(), value.clone());
}
}
FieldOp::Create {
entity: e,
id: i,
data,
} if e == entity && *i == id => {
s = Some(data.clone());
}
FieldOp::Delete {
entity: e,
id: i,
} if e == entity && *i == id => {
s = None;
}
_ => {}
}
}
s
}