Add empty transport planning in DirectorWorker: Introduced SQL queries and logic to handle empty transports for vehicle retrieval when no transport vehicles are available in the current branch. Enhanced the planning process to check for better selling prices in other branches and available free vehicles, improving transport management and operational efficiency.
This commit is contained in:
@@ -264,6 +264,41 @@ const QUERY_INSERT_TRANSPORT: &str = r#"
|
|||||||
VALUES ($1, $2, $3, $4, $5, NOW(), NOW());
|
VALUES ($1, $2, $3, $4, $5, NOW(), NOW());
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
|
// Leere Transporte (product_id = NULL, size = 0) zum Zurückholen von Fahrzeugen
|
||||||
|
const QUERY_INSERT_EMPTY_TRANSPORT: &str = r#"
|
||||||
|
INSERT INTO falukant_data.transport
|
||||||
|
(source_region_id, target_region_id, product_id, size, vehicle_id, created_at, updated_at)
|
||||||
|
VALUES ($1, $2, NULL, 0, $3, NOW(), NOW());
|
||||||
|
"#;
|
||||||
|
|
||||||
|
// Alle Branches des Users mit ihren Regionen
|
||||||
|
const QUERY_GET_USER_BRANCHES: &str = r#"
|
||||||
|
SELECT DISTINCT
|
||||||
|
b.region_id,
|
||||||
|
b.id AS branch_id
|
||||||
|
FROM falukant_data.branch b
|
||||||
|
WHERE b.falukant_user_id = $1
|
||||||
|
AND b.region_id != $2;
|
||||||
|
"#;
|
||||||
|
|
||||||
|
// Freie Transportmittel in einer Region (nicht in aktiven Transporten)
|
||||||
|
const QUERY_GET_FREE_VEHICLES_IN_REGION: &str = r#"
|
||||||
|
SELECT
|
||||||
|
v.id AS vehicle_id,
|
||||||
|
vt.capacity AS capacity
|
||||||
|
FROM falukant_data.vehicle v
|
||||||
|
JOIN falukant_type.vehicle vt
|
||||||
|
ON vt.id = v.vehicle_type_id
|
||||||
|
WHERE v.falukant_user_id = $1
|
||||||
|
AND v.region_id = $2
|
||||||
|
AND v.id NOT IN (
|
||||||
|
SELECT DISTINCT t.vehicle_id
|
||||||
|
FROM falukant_data.transport t
|
||||||
|
WHERE t.vehicle_id IS NOT NULL
|
||||||
|
AND t.arrived_at IS NULL
|
||||||
|
);
|
||||||
|
"#;
|
||||||
|
|
||||||
const QUERY_GET_SALARY_TO_PAY: &str = r#"
|
const QUERY_GET_SALARY_TO_PAY: &str = r#"
|
||||||
SELECT d.id, d.employer_user_id, d.income
|
SELECT d.id, d.employer_user_id, d.income
|
||||||
FROM falukant_data.director d
|
FROM falukant_data.director d
|
||||||
@@ -691,19 +726,80 @@ impl DirectorWorker {
|
|||||||
director.id, director.branch_id, items.len()
|
director.id, director.branch_id, items.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Falls es nichts zu transportieren gibt, können wir sofort zurückkehren.
|
// Für alle Items dieses Directors sollten die user_id-Felder identisch
|
||||||
|
// sein (Arbeitgeber des Directors).
|
||||||
|
let falukant_user_id = if items.is_empty() {
|
||||||
|
// Wenn keine Items vorhanden sind, müssen wir die user_id anders ermitteln
|
||||||
|
const QUERY_GET_DIRECTOR_USER: &str = r#"
|
||||||
|
SELECT employer_user_id
|
||||||
|
FROM falukant_data.director
|
||||||
|
WHERE id = $1;
|
||||||
|
"#;
|
||||||
|
conn.prepare("get_director_user", QUERY_GET_DIRECTOR_USER)?;
|
||||||
|
let user_rows = conn.execute("get_director_user", &[&director.id])?;
|
||||||
|
user_rows
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.and_then(|row| row.get("employer_user_id").and_then(|v| v.parse::<i32>().ok()))
|
||||||
|
.ok_or_else(|| DbError::new("Konnte employer_user_id nicht ermitteln"))?
|
||||||
|
} else {
|
||||||
|
items[0].user_id
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prüfe, ob Transportmittel im aktuellen Branch vorhanden sind
|
||||||
|
const QUERY_COUNT_VEHICLES_IN_BRANCH_REGION: &str = r#"
|
||||||
|
SELECT COUNT(*) AS count
|
||||||
|
FROM falukant_data.vehicle v
|
||||||
|
JOIN falukant_data.branch b
|
||||||
|
ON b.region_id = v.region_id
|
||||||
|
WHERE v.falukant_user_id = $1
|
||||||
|
AND b.id = $2
|
||||||
|
AND v.id NOT IN (
|
||||||
|
SELECT DISTINCT t.vehicle_id
|
||||||
|
FROM falukant_data.transport t
|
||||||
|
WHERE t.vehicle_id IS NOT NULL
|
||||||
|
AND t.arrived_at IS NULL
|
||||||
|
);
|
||||||
|
"#;
|
||||||
|
|
||||||
|
conn.prepare("count_vehicles_in_branch", QUERY_COUNT_VEHICLES_IN_BRANCH_REGION)?;
|
||||||
|
let vehicle_count_rows = conn.execute(
|
||||||
|
"count_vehicles_in_branch",
|
||||||
|
&[&falukant_user_id, &director.branch_id],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let vehicles_in_branch = vehicle_count_rows
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.and_then(|row| row.get("count").and_then(|v| v.parse::<i32>().ok()))
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
// Falls es nichts zu transportieren gibt, prüfe auf leere Transporte
|
||||||
if items.is_empty() {
|
if items.is_empty() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[DirectorWorker] Keine Inventar-Items für Transporte gefunden für Director {} (branch_id={})",
|
"[DirectorWorker] Keine Inventar-Items für Transporte gefunden für Director {} (branch_id={})",
|
||||||
director.id, director.branch_id
|
director.id, director.branch_id
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Wenn keine Transportmittel im Branch vorhanden sind, versuche leere Transporte zu planen
|
||||||
|
if vehicles_in_branch == 0 {
|
||||||
|
eprintln!(
|
||||||
|
"[DirectorWorker] Keine Transportmittel im Branch {} vorhanden, prüfe auf leere Transporte zum Zurückholen",
|
||||||
|
director.branch_id
|
||||||
|
);
|
||||||
|
if let Err(err) = self.plan_empty_transports_for_vehicle_retrieval(
|
||||||
|
&mut conn,
|
||||||
|
falukant_user_id,
|
||||||
|
director.branch_id,
|
||||||
|
) {
|
||||||
|
eprintln!(
|
||||||
|
"[DirectorWorker] Fehler beim Planen leerer Transporte: {err}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Für alle Items dieses Directors sollten die user_id-Felder identisch
|
|
||||||
// sein (Arbeitgeber des Directors).
|
|
||||||
let falukant_user_id = items[0].user_id;
|
|
||||||
|
|
||||||
// Lohnende Transporte planen. Dabei werden:
|
// Lohnende Transporte planen. Dabei werden:
|
||||||
// - ggf. Transport-Einträge erzeugt
|
// - ggf. Transport-Einträge erzeugt
|
||||||
// - Inventar-Mengen reduziert
|
// - Inventar-Mengen reduziert
|
||||||
@@ -743,6 +839,24 @@ impl DirectorWorker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nach normalen Transporten: Wenn keine Transportmittel mehr im Branch vorhanden sind,
|
||||||
|
// aber bessere Verkaufspreise in anderen Branches existieren, plane leere Transporte
|
||||||
|
if vehicles_in_branch == 0 {
|
||||||
|
eprintln!(
|
||||||
|
"[DirectorWorker] Keine Transportmittel mehr im Branch {} vorhanden, prüfe auf leere Transporte zum Zurückholen",
|
||||||
|
director.branch_id
|
||||||
|
);
|
||||||
|
if let Err(err) = self.plan_empty_transports_for_vehicle_retrieval(
|
||||||
|
&mut conn,
|
||||||
|
falukant_user_id,
|
||||||
|
director.branch_id,
|
||||||
|
) {
|
||||||
|
eprintln!(
|
||||||
|
"[DirectorWorker] Fehler beim Planen leerer Transporte: {err}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1235,6 +1349,195 @@ impl DirectorWorker {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Plant leere Transporte, um Fahrzeuge zurückzuholen, wenn:
|
||||||
|
/// - Keine Transportmittel im aktuellen Branch vorhanden sind
|
||||||
|
/// - Aber bessere Verkaufspreise in anderen Branches existieren
|
||||||
|
/// - Freie Transportmittel in anderen Regionen verfügbar sind
|
||||||
|
fn plan_empty_transports_for_vehicle_retrieval(
|
||||||
|
&mut self,
|
||||||
|
conn: &mut DbConnection,
|
||||||
|
falukant_user_id: i32,
|
||||||
|
current_branch_id: i32,
|
||||||
|
) -> Result<(), DbError> {
|
||||||
|
// Aktuelle Branch-Region ermitteln
|
||||||
|
const QUERY_GET_BRANCH_REGION: &str = r#"
|
||||||
|
SELECT region_id
|
||||||
|
FROM falukant_data.branch
|
||||||
|
WHERE id = $1;
|
||||||
|
"#;
|
||||||
|
|
||||||
|
conn.prepare("get_branch_region", QUERY_GET_BRANCH_REGION)?;
|
||||||
|
let branch_rows = conn.execute("get_branch_region", &[¤t_branch_id])?;
|
||||||
|
|
||||||
|
let current_region_id = match branch_rows.into_iter().next() {
|
||||||
|
Some(row) => row
|
||||||
|
.get("region_id")
|
||||||
|
.and_then(|v| v.parse::<i32>().ok())
|
||||||
|
.ok_or_else(|| DbError::new("Konnte region_id nicht ermitteln"))?,
|
||||||
|
None => return Ok(()), // Branch nicht gefunden, nichts zu tun
|
||||||
|
};
|
||||||
|
|
||||||
|
// Alle anderen Branches des Users finden
|
||||||
|
conn.prepare("get_user_branches", QUERY_GET_USER_BRANCHES)?;
|
||||||
|
let branch_rows = conn.execute(
|
||||||
|
"get_user_branches",
|
||||||
|
&[&falukant_user_id, ¤t_region_id],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if branch_rows.is_empty() {
|
||||||
|
eprintln!(
|
||||||
|
"[DirectorWorker] Keine anderen Branches für User {} gefunden",
|
||||||
|
falukant_user_id
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Für jeden anderen Branch prüfen, ob bessere Verkaufspreise existieren
|
||||||
|
// und freie Transportmittel verfügbar sind
|
||||||
|
conn.prepare("get_free_vehicles_in_region", QUERY_GET_FREE_VEHICLES_IN_REGION)?;
|
||||||
|
conn.prepare("get_region_worth_for_product", QUERY_GET_REGION_WORTH_FOR_PRODUCT)?;
|
||||||
|
conn.prepare("insert_empty_transport", QUERY_INSERT_EMPTY_TRANSPORT)?;
|
||||||
|
|
||||||
|
// Prüfe für jeden Branch, ob es bessere Verkaufspreise gibt
|
||||||
|
// Dazu müssen wir alle Produkte durchgehen, die in town_product_worth existieren
|
||||||
|
const QUERY_GET_ALL_PRODUCTS: &str = r#"
|
||||||
|
SELECT DISTINCT product_id
|
||||||
|
FROM falukant_data.town_product_worth
|
||||||
|
WHERE region_id IN (
|
||||||
|
SELECT region_id
|
||||||
|
FROM falukant_data.branch
|
||||||
|
WHERE falukant_user_id = $1
|
||||||
|
);
|
||||||
|
"#;
|
||||||
|
|
||||||
|
conn.prepare("get_all_products", QUERY_GET_ALL_PRODUCTS)?;
|
||||||
|
let product_rows = conn.execute("get_all_products", &[&falukant_user_id])?;
|
||||||
|
|
||||||
|
let mut best_target_branch: Option<(i32, i32)> = None; // (branch_id, region_id)
|
||||||
|
let mut best_price_delta: f64 = 0.0;
|
||||||
|
|
||||||
|
for branch_row in &branch_rows {
|
||||||
|
let target_region_id = branch_row
|
||||||
|
.get("region_id")
|
||||||
|
.and_then(|v| v.parse::<i32>().ok())
|
||||||
|
.unwrap_or(-1);
|
||||||
|
let target_branch_id = branch_row
|
||||||
|
.get("branch_id")
|
||||||
|
.and_then(|v| v.parse::<i32>().ok())
|
||||||
|
.unwrap_or(-1);
|
||||||
|
|
||||||
|
if target_region_id < 0 || target_branch_id < 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe für jedes Produkt, ob der Preis in der Zielregion besser ist
|
||||||
|
for product_row in &product_rows {
|
||||||
|
let product_id = product_row
|
||||||
|
.get("product_id")
|
||||||
|
.and_then(|v| v.parse::<i32>().ok())
|
||||||
|
.unwrap_or(-1);
|
||||||
|
|
||||||
|
if product_id < 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Worth-Percent für beide Regionen abrufen
|
||||||
|
let worth_rows = conn.execute(
|
||||||
|
"get_region_worth_for_product",
|
||||||
|
&[&falukant_user_id, &product_id],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut current_worth: Option<f64> = None;
|
||||||
|
let mut target_worth: Option<f64> = None;
|
||||||
|
|
||||||
|
for worth_row in worth_rows {
|
||||||
|
let region_id = worth_row
|
||||||
|
.get("region_id")
|
||||||
|
.and_then(|v| v.parse::<i32>().ok())
|
||||||
|
.unwrap_or(-1);
|
||||||
|
let worth_percent = worth_row
|
||||||
|
.get("worth_percent")
|
||||||
|
.and_then(|v| v.parse::<f64>().ok())
|
||||||
|
.unwrap_or(100.0);
|
||||||
|
|
||||||
|
if region_id == current_region_id {
|
||||||
|
current_worth = Some(worth_percent);
|
||||||
|
} else if region_id == target_region_id {
|
||||||
|
target_worth = Some(worth_percent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wenn der Zielregion einen besseren Preis hat, merke diesen Branch
|
||||||
|
if let (Some(current), Some(target)) = (current_worth, target_worth) {
|
||||||
|
let price_delta = target - current;
|
||||||
|
if price_delta > best_price_delta {
|
||||||
|
best_price_delta = price_delta;
|
||||||
|
best_target_branch = Some((target_branch_id, target_region_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wenn ein Branch mit besseren Preisen gefunden wurde, prüfe auf freie Transportmittel
|
||||||
|
if let Some((target_branch_id, target_region_id)) = best_target_branch {
|
||||||
|
eprintln!(
|
||||||
|
"[DirectorWorker] Besserer Verkaufspreis in Branch {} (Region {}) gefunden, prüfe auf freie Transportmittel",
|
||||||
|
target_branch_id, target_region_id
|
||||||
|
);
|
||||||
|
|
||||||
|
// Freie Transportmittel in der Zielregion finden
|
||||||
|
let vehicle_rows = conn.execute(
|
||||||
|
"get_free_vehicles_in_region",
|
||||||
|
&[&falukant_user_id, &target_region_id],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if vehicle_rows.is_empty() {
|
||||||
|
eprintln!(
|
||||||
|
"[DirectorWorker] Keine freien Transportmittel in Region {} gefunden",
|
||||||
|
target_region_id
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe, ob eine Route zurück zum aktuellen Branch existiert
|
||||||
|
let vehicles = Self::get_transport_vehicles_for_route(
|
||||||
|
conn,
|
||||||
|
falukant_user_id,
|
||||||
|
target_region_id,
|
||||||
|
current_region_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if vehicles.is_empty() {
|
||||||
|
eprintln!(
|
||||||
|
"[DirectorWorker] Keine Route von Region {} zurück zu Region {} gefunden",
|
||||||
|
target_region_id, current_region_id
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leere Transporte für alle verfügbaren Fahrzeuge anlegen
|
||||||
|
let mut transport_count = 0;
|
||||||
|
for vehicle in &vehicles {
|
||||||
|
conn.execute(
|
||||||
|
"insert_empty_transport",
|
||||||
|
&[&target_region_id, ¤t_region_id, &vehicle.id],
|
||||||
|
)?;
|
||||||
|
transport_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln!(
|
||||||
|
"[DirectorWorker] {} leere Transporte geplant: Region {} -> Region {}",
|
||||||
|
transport_count, target_region_id, current_region_id
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
eprintln!(
|
||||||
|
"[DirectorWorker] Kein Branch mit besseren Verkaufspreisen gefunden"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn update_inventory_quantity(
|
fn update_inventory_quantity(
|
||||||
conn: &mut DbConnection,
|
conn: &mut DbConnection,
|
||||||
inventory_id: i32,
|
inventory_id: i32,
|
||||||
|
|||||||
Reference in New Issue
Block a user