diff --git a/server/src/tables_data/handlers/post_table_data.rs b/server/src/tables_data/handlers/post_table_data.rs index bdfdb03..85a0479 100644 --- a/server/src/tables_data/handlers/post_table_data.rs +++ b/server/src/tables_data/handlers/post_table_data.rs @@ -14,18 +14,17 @@ pub async fn post_table_data( let profile_name = request.profile_name; let table_name = request.table_name; let mut data = HashMap::new(); - + // Process and validate all data values for (key, value) in request.data { let trimmed = value.trim().to_string(); - + // Handle firma specially - it cannot be empty if key == "firma" && trimmed.is_empty() { return Err(Status::invalid_argument("Firma cannot be empty")); } - + // Add trimmed non-empty values to data map - // Empty optional fields will be skipped in SQL generation if !trimmed.is_empty() || key == "firma" { data.insert(key, trimmed); } @@ -44,7 +43,7 @@ pub async fn post_table_data( // Lookup table_definition let table_def = sqlx::query!( - r#"SELECT id, columns, linked_table_id FROM table_definitions + r#"SELECT id, columns FROM table_definitions WHERE profile_id = $1 AND table_name = $2"#, profile_id, table_name @@ -70,18 +69,22 @@ pub async fn post_table_data( columns.push((name, sql_type)); } - // Check required system columns - let mut required_columns = vec!["firma".to_string()]; - if let Some(linked_table_id) = table_def.linked_table_id { - let linked_table = sqlx::query!( - "SELECT table_name FROM table_definitions WHERE id = $1", - linked_table_id - ) - .fetch_one(db_pool) - .await - .map_err(|e| Status::internal(format!("Linked table error: {}", e)))?; + // Get required relationships + let required_links = sqlx::query!( + r#"SELECT ltd.table_name + FROM table_definition_links tdl + JOIN table_definitions ltd ON tdl.linked_table_id = ltd.id + WHERE tdl.source_table_id = $1 AND tdl.is_required = true"#, + table_def.id + ) + .fetch_all(db_pool) + .await + .map_err(|e| Status::internal(format!("Link lookup error: {}", e)))?; - let base_name = linked_table.table_name.splitn(2, '_').last().unwrap_or(&linked_table.table_name); + // Build required columns list + let mut required_columns = vec!["firma".to_string()]; + for link in required_links { + let base_name = link.table_name.split('_').last().unwrap_or(&link.table_name); required_columns.push(format!("{}_id", base_name)); } @@ -101,7 +104,6 @@ pub async fn post_table_data( } } - // Validate Steel scripts let scripts = sqlx::query!( "SELECT target_column, script FROM table_scripts WHERE table_definitions_id = $1", @@ -114,50 +116,41 @@ pub async fn post_table_data( for script_record in scripts { let target_column = script_record.target_column; - // Check if target column is present in data if !data.contains_key(&target_column) { return Err(Status::invalid_argument( format!("Column '{}' is required due to an associated script", target_column) )); } - // Parse the script let operation = execution::parse_script(&script_record.script, &target_column) .map_err(|e| Status::invalid_argument(e.to_string()))?; - // Get source column from operation - let source_column = - match operation { - ScriptOperation::SetToLocalColumn { source } => source, - ScriptOperation::SetToExternalColumn { table, column } => { - // For external columns, we need to resolve the value from the external table - let external_source = format!("@{}.{}", table, column); - let resolved_value = execution::resolve_value( - db_pool, - profile_id, - &table_name, - &data, - &external_source - ).await.map_err(|e| Status::invalid_argument(e.to_string()))?; - - // Return the resolved value - resolved_value - } - }; + let source_column = match operation { + ScriptOperation::SetToLocalColumn { source } => source, + ScriptOperation::SetToExternalColumn { table, column } => { + let external_source = format!("@{}.{}", table, column); + let resolved_value = execution::resolve_value( + db_pool, + profile_id, + &table_name, + &data, + &external_source + ).await.map_err(|e| Status::invalid_argument(e.to_string()))?; + + resolved_value + } + }; - // Check source column presence let source_value = data.get(&source_column) .ok_or_else(|| Status::invalid_argument( format!("Source column '{}' required by script for '{}' is missing", source_column, target_column) ))?; - // Get target value let target_value = data.get(&target_column) .ok_or_else(|| Status::invalid_argument( format!("Target column '{}' is missing in data", target_column) ))?; - // Validate value match if target_value != source_value { return Err(Status::invalid_argument( format!("Value for '{}' must match '{}' as per script. Expected '{}', got '{}'", @@ -186,8 +179,6 @@ pub async fn post_table_data( .ok_or_else(|| Status::invalid_argument(format!("Column not found: {}", col)))? }; - // TODO This needs heavy adjustement. More stuff to be added for user to only pick - // preprogrammed functions match sql_type { "TEXT" | "VARCHAR(15)" | "VARCHAR(255)" => { if let Some(max_len) = sql_type.strip_prefix("VARCHAR(") @@ -221,7 +212,6 @@ pub async fn post_table_data( param_idx += 1; } - // Ensure we have at least one column to insert if columns_list.is_empty() { return Err(Status::invalid_argument("No valid columns to insert")); }