diff --git a/src/worker/director.rs b/src/worker/director.rs index c3d3555..4547517 100644 --- a/src/worker/director.rs +++ b/src/worker/director.rs @@ -370,11 +370,42 @@ impl DirectorWorker { conn: &mut DbConnection, plan: &ProductionPlan, ) -> Result<(), DbError> { - let free_capacity = Self::calc_free_capacity(plan); + // Hole aktuelle Kapazitätswerte direkt aus der DB, um Race Conditions zu vermeiden + let capacity_rows = conn.execute("get_branch_capacity", &[&plan.branch_id])?; + if capacity_rows.is_empty() { + eprintln!("[DirectorWorker] Keine Kapazitätsdaten für Branch {} gefunden", plan.branch_id); + return Ok(()); + } + + let row = &capacity_rows[0]; + let stock_size: i32 = row + .get("stock_size") + .and_then(|v| v.parse::().ok()) + .unwrap_or(0); + let used_in_stock: i32 = row + .get("used_in_stock") + .and_then(|v| v.parse::().ok()) + .unwrap_or(0); + let running_productions_quantity: i32 = row + .get("running_productions_quantity") + .and_then(|v| v.parse::().ok()) + .unwrap_or(0); + + // Berechne freie Kapazität mit aktuellen Werten + let free_capacity = stock_size - used_in_stock - running_productions_quantity; + + if free_capacity <= 0 { + eprintln!( + "[DirectorWorker] Keine Produktion gestartet: Kein freier Lagerplatz (stock_size={}, used={}, running_qty={})", + stock_size, used_in_stock, running_productions_quantity + ); + return Ok(()); + } + let one_piece_cost = Self::calc_one_piece_cost(plan); let max_money_production = Self::calc_max_money_production(plan, one_piece_cost); - let to_produce = (free_capacity.min(max_money_production)).clamp(0, 100); + let to_produce = (free_capacity.min(max_money_production)).clamp(0, 100); eprintln!( "[DirectorWorker] Produktionsberechnung: free_capacity={}, one_piece_cost={}, max_money_production={}, to_produce={}, running_productions={}", @@ -437,9 +468,6 @@ impl DirectorWorker { Ok(()) } - fn calc_free_capacity(plan: &ProductionPlan) -> i32 { - plan.stock_size - plan.used_in_stock - plan.running_productions_quantity - } fn calc_one_piece_cost(plan: &ProductionPlan) -> f64 { (plan.certificate * 6) as f64 diff --git a/src/worker/sql.rs b/src/worker/sql.rs index 36cdfc1..adeea95 100644 --- a/src/worker/sql.rs +++ b/src/worker/sql.rs @@ -1419,8 +1419,26 @@ pub const QUERY_GET_FINISHED_PRODUCTIONS: &str = r#" LEFT JOIN falukant_type.product_weather_effect pwe ON pwe.product_id = p.product_id AND pwe.weather_type_id = w.weather_type_id + -- Prüfe, ob genug freier Lagerplatz vorhanden ist + LEFT JOIN ( + SELECT + br2.id AS branch_id, + COALESCE(SUM(fds.quantity), 0) AS stock_size, + COALESCE(SUM(COALESCE(fdi.quantity, 0)), 0) AS used_in_stock, + COALESCE(SUM(COALESCE(fdp2.quantity, 0)), 0) AS running_productions_quantity + FROM falukant_data.branch br2 + LEFT JOIN falukant_data.stock fds ON fds.branch_id = br2.id + LEFT JOIN falukant_data.inventory fdi ON fdi.stock_id = fds.id + LEFT JOIN falukant_data.production fdp2 ON fdp2.branch_id = br2.id + GROUP BY br2.id + ) capacity ON capacity.branch_id = p.branch_id -- Wetter-Effekte derzeit aus der Qualitätsberechnung entfernt WHERE p.start_timestamp + INTERVAL '1 minute' * pr.production_time <= NOW() + -- Nur Produktionen zurückgeben, für die genug Lagerplatz vorhanden ist + -- running_productions_quantity enthält bereits p.quantity, daher prüfen wir: + -- (stock_size - used_in_stock - running_productions_quantity) >= 0 + -- Das bedeutet: Nach Abzug aller laufenden Produktionen muss noch Platz vorhanden sein + AND (capacity.stock_size - capacity.used_in_stock - capacity.running_productions_quantity) >= 0 ORDER BY p.start_timestamp; "#;