Fertigungsauftrag verbraucht keine Komponenten aus Unterbaugruppen

Hallo zusammen, ich habe folgende Fragen zur Lagerhaltung und Fertigungsaufträgen,

bei einem Fertigungsauftrag für ein Endprodukt mit mehrstufiger BOM (verschachtelte Stücklisten) werden nur direkte Materialien vom Lager abgezogen. Komponenten aus Unterbaugruppen der zweiten BOM-Ebene bleiben unverändert.

Beispiel:

  • Endprodukt: „Gehäuse komplett“

  • BOM: Unterbaugruppe „Rahmen“ (hat eigene BOM: Schrauben + Bleche) + „Glas“

  • Nur „Glas“ wird verbraucht, „Schrauben/Bleche“ aus „Rahmen“ nicht.

Fragen:

  1. Unterstützt Dolibarr Standard-rekursive BOM-Auflösung?

  2. Welche Einstellung wird benötigt?

  3. Gibt es einen Workaround ohne manuelle Nachbuchung?

Kontext:
Mehrstufige Fertigung (3–4 Ebenen), manuelle Nachbuchung ist fehleranfällig daher nicht akzeptabel.

Beste Grüße
Gerhard

Hallo Gerhard,

die von dir beschriebene Arbeitsweise nutze ich täglich in einem produzierenden Betrieb.
BOM-in BOM. Anfangs dachte ich auch es müsste doch die Komponenten bis in den letzten Zweig buchen.
Mittlerweile denke ich dies wäre aber falsch.
Auch wenn man ein Endprodukt “Gehäuse komplett” hat müssen zuerste die Unterbaugruppen gefertigt werden. Diese BOMs benötigen eigene MOs. Damit sie produziert werden können.
Sobald diese produziert sind kann auch das Endprodukt produziert werden.

Um aber zu sehen ob auch alle Teile die für die Produktion “Gehäuse komplett” benötigt werden tatsächlich am Lager sind, habe ich mir selbst ein Abfrage Modul erstellt.

So ist es möglich BOMs in BOMs bis zum kleinsten Teil aufzugliedern.
Ein tolles Tool wenn es um die Visualisierung geht ist: BomHierarchy

Wenn du eine Idee für einen anderen Lösungsansatz hast bin ich gerne bereit daran mitzuwirken.

VG,
Kim

Hallo Kim,

vielen Dank für Deine schnelle Rückmeldung und die interessanten Einblicke in Deine Praxis. Die Visualisierungslösung BomHierarchy sieht tatsächlich vielversprechend aus.

Meine Herausforderung liegt allerdings auf einer anderen Ebene: Unser Prozess erfordert, dass bei der Auslösung eines einzelnen Fertigungsauftrags (MO) für das Endprodukt automatisch alle benötigten Ausgangsmaterialien über alle Verschachtelungsebenen hinweg gebucht werden – also eine vollständige, rekursive Auflösung der gesamten Stücklistenstruktur in einer Transaktion.

Das Kernproblem in unserem Anwendungsfall:
Dolibarr behandelt Unterbaugruppen als „Blackboxen“ und löst deren interne BOM-Strukturen bei einem MO nicht automatisch auf. Dadurch erfolgt keine Lagerbuchung für die tieferliegenden Komponenten – obwohl diese physisch verbraucht werden.

Die von Dir beschriebene Vorgehensweise (separate MOs pro Unterbaugruppe) ist zwar grundsätzlich nachvollziehbar, wird in unserem skalierenden Produktionsumfeld jedoch zunehmend fehleranfällig und nicht prozessstabil:

  • Bei mehreren Verschachtelungsebenen steigt der manuelle Koordinierungsaufwand exponentiell.
  • Die Lagerbestandsführung verliert ihre Echtzeitaktualität.
  • Das Risiko von Buchungsfehlern oder vergessenen Teilaufträgen ist hoch.

Meine konkrete Frage an die Community:
Gibt es eine automatisierbare Lösung (Modul, Hook, Skript oder Konfiguration), mit der Dolibarr mehrstufige BOMs rekursiv auflösen und in einem einzigen Fertigungsauftrag alle Komponenten über alle Ebenen buchen kann?

Falls nicht – welche Workarounds oder externen Tools nutzt ihr für ähnliche Anforderungen?

Vielen Dank im Voraus für weitere Erfahrungsberichte oder Lösungsansätze!

Beste Grüße,
Gerhard

Hallo Gerhard,

vielen Dank für die noch ausführlichere Erklärung. MOs und BOMs sind meiner Meinung nach noch sehr “Stiefmütterlich” in Dolibarr behandelt.

Ich würde aber dennoch sage, dass die Arbeitsweise mit MOs für Unterbaugruppen sehr prozessstabil ist. Vor allem wenn man an eine Serienproduktion denkt.
Bei in serie gefertigten Produkten ist es durchaus praktikabel zuerst alle Unterbaugruppen herzustellen und dann zu einem Endprodukt zusammenzufügen.

Für mich klingt dein Fall so als ob zwar “Unterbaugruppen” in BOM vorhanden sind aber meist direkt das Endprodukt gerfertigt wird.
Für einen solchen anwendungsfall könnte ich mir sehr gut ein Modul vorstellen, welches auf Knopfdruck alle BOMs innerhalb eines Fertigungsauftrags auflöst und so in den Fertigungsauftrag übernimmt. Also innerhalb der BOM gibt es BOM-in BOM. Aber dann im Fertigungsauftrag gibt es nur noch eine lange Liste mit allen Teilen.
Wäre das in etwa der Ansatz?

Das liese sich sicher relativ gut umsetzen.
Viele Grüße,
Kim

1 „Gefällt mir“

Hallo Gerhard,

ich denke, dass die Herangehensweise von Dolibarr in diesem Fall korrekt ist. Denn es ist so, dass z.B. eine Produkt welches sich in deiner “Haupt-BOM” befindet und eine eigene BOM hat über verschiedene Wege beschafft werden kann. Also z.B. könnte es sein, dass das Produkt von einem Lieferant als fertiges Produkt zugekauft wird oder dass es intern unterschiedliche BOM´s für ein und das selbe Produkt gibt, wg. verschiedener Herstellungsverfahren.

Ich denke für dich als Lösung gibt es da nur den Weg, dass du dir eine BOM für das “Haupt-Produkt” erstellst welche alle Unterteile beinhaltet.

Gruß Christian

1 „Gefällt mir“

Hallo Kim,

danke für deine hilfreichen Gedanken – sie haben mir weitergeholfen, das Problem besser zu verstehen.

Ich finde deinen Ansatz eines „BOM-auflösen“-Moduls überzeugend und passend für unseren Anwendungsfall. Bevor wir jedoch möglicherweise einen Entwickler beauftragen oder ein kostenpflichtiges Modul suchen, möchte ich erst einmal selbst versuchen, eine einfache Lösung zu programmieren.

Meine möglichen nächsten Schritte wären:

  1. Ein PHP-Skript zu schreiben, das per Knopfdruck alle Unterbaugruppen eines MOs rekursiv auflöst
  2. Eine Vorschau-Funktion, die zeigt, welche Komponenten auf welche Ebene aufgelöst werden
  3. Die Möglichkeit, diese Komponenten automatisch in den MO zu übernehmen

Daher meine konkrete Bitte an dich/die Community:

  • Gibt es bereits Code-Schnipsel, Hooks oder Teil-Lösungen, die mir als Startpunkt dienen könnten?
  • Insbesondere für die rekursive BOM-Abfrage und das Hinzufügen von MO-Positionen?
  • Hast du technische Tipps zu Dolibarr-APIs oder Tabellen-Strukturen?

Vielen Dank für jede kleine Code-Hilfe oder Datenbank-Hinweise.

Beste Grüße,
Gerhard

Guten Morgen Gerhard,

Christian hat den Nagel auf den Kopf getroffen. Der Punkt das natürlich Unterbaugruppen evtl. ganz anders bezogen.
Aber für deine Anwendung lässt sich ja auch eine Lösung finden.
Hoffe du verwendest ein Tool wie MySQL Workbench oder Heidi SQL damit ist es sehr einfach nachvollziehbar.

Die Fertigungsaufträge sind in der Tabelle: llx_mrp_mo gespeichert. Dort stehen z.B. in fk_produkt der Link zu welches Produkt überhaupt gefertigt werden soll.
Die Spalte fk_bom enthält den Link zur Stückliste (BOM).

Stücklisten (Grunddaten) sind in llx_bom_bom abgelegt. Die einzelnen Elemente (lines) in llx_bom_bomline.

hier müsste deine Erweiterung ansetzen und auflösen, dass wenn in in einer Zeile ein weiter BOM steht diese dann auch aufgelöst wird.

Ich habe mir ein Tool erstellt, mit dem ich Produkte aus allen BOMs in denen es vorkommt austauschen kann. Typischer Anwendungsfall: Schraube A wird durch Schraube B ersetzt. Nun möchte man ja nicht alle BOMs in denen die Schraube vorkommt manuell ändern.
Von den SQL Abfragen her dürfte das ziemlich ähnlich sein.

foreach ($selected_boms as $bom_id) {
// Get affected lines details before update for logging
$sql_lines = "SELECT rowid, qty FROM ".MAIN_DB_PREFIX.„bom_bomline“;
$sql_lines .= " WHERE fk_bom = ".((int) $bom_id);
$sql_lines .= " AND fk_product = ".((int) $search_product_id);

$resql_lines = $db->query($sql_lines);
$affected_lines = array();
if ($resql_lines) {
	while ($obj = $db->fetch_object($resql_lines)) {
		$affected_lines\[\] = array('rowid' => $obj->rowid, 'qty' => $obj->qty);
	}
}

// Update BOM lines - replace search_product with replace_product, keeping same quantity
$sql = "UPDATE ".MAIN_DB_PREFIX."bom_bomline";
$sql .= " SET fk_product = ".((int) $replace_product_id);
$sql .= " WHERE fk_bom = ".((int) $bom_id);
$sql .= " AND fk_product = ".((int) $search_product_id);

$resql = $db->query($sql);
if ($resql) {
	$affected = $db->affected_rows($resql);
	if ($affected > 0) {
		$updated_count++;
		$lines_updated += $affected;
		
		// Log each affected line
		foreach ($affected_lines as $line) {
			$sql_log = "INSERT INTO ".MAIN_DB_PREFIX."masschangebom_log";
			$sql_log .= " (date_creation, fk_user, fk_bom, old_product_id, new_product_id,";
			$sql_log .= " old_product_ref, new_product_ref, old_product_label, new_product_label, qty)";
			$sql_log .= " VALUES (";
			$sql_log .= "'".$db->idate(dol_now())."',";
			$sql_log .= " ".((int) $user->id).",";
			$sql_log .= " ".((int) $bom_id).",";
			$sql_log .= " ".((int) $search_product_id).",";
			$sql_log .= " ".((int) $replace_product_id).",";
			$sql_log .= " '".$db->escape($old_product->ref)."',";
			$sql_log .= " '".$db->escape($new_product->ref)."',";
			$sql_log .= " '".$db->escape($old_product->label)."',";
			$sql_log .= " '".$db->escape($new_product->label)."',";
			$sql_log .= " ".((float) $line\['qty'\]);
			$sql_log .= ")";
			
			$resql_log = $db->query($sql_log);
			if (!$resql_log) {
				$error++;
				setEventMessages($db->lasterror(), null, 'errors');
			}
		}
	}
} else {
	$error++;
	setEventMessages($db->lasterror(), null, 'errors');
	break;
}

}

if (!$error) {
$db->commit();
setEventMessages($langs->trans(„MassChangeBomSuccess“, $updated_count).’ - ‚.$lines_updated.‘ '.$langs->trans(„LinesUpdated“), null, ‚mesgs‘);
header(„Location: „.$_SERVER[„PHP_SELF“].“?search_product_id=“.$search_product_id);
exit;
} else {
$db->rollback();
}

}

1 „Gefällt mir“

Hallo Kim,

vielen Dank für das Code-Beispiel und die nützlichen Hinweise. Ich will sehen wie tatsächlich der Anwendungsfall im Detail definiert wird und ob wir bei dem Dolibarr-Standard bleiben können. Tatsächlich hat der bereits implementierte Workflow auch seine Vorteile in Hinsicht der Flexibilität.

Beste Grüße

Gerhard

Hallo Christian,

danke für deine Perspektive – das macht Sinn. Du hast recht, Dolibarrs Ansatz ist logisch, wenn man unterschiedliche Beschaffungswege oder Herstellungsverfahren für Unterbaugruppen berücksichtigen will.

Für meinen konkreten Anwendungsfall bleibt aber möglicherweise die Herausforderung: Wir fertigen alles selbst und direkt in einem Zug. Eine flache BOM mit allen Einzelteilen wäre zwar theoretisch möglich, aber bei komplexen Produkten mit vielen Verschachtelungen unübersichtlich und wartungsintensiv.

Danke für den Input!

Gruß, Gerhard

Hallo Gerhard,

wären BOM-IN-BOM Verschachtelungen nicht genau das, was du suchst?

In einer BOM kannst du dann entweder BOMs oder Produkte auswählen. Diese werden dir bei einem MO auch aufgefächert angezeigt und du kannst auswählen, ob für die Sub-BOMs automatisch Fertigungsaufträge angelegt werden sollen.

Vlt. hilft der Einwurf noch.

Gruß
Niklas

Danke dir Niklas für den Hinweis.

Es ging um einen Fertigungsauftrag für ein Endprodukt. Der Verbrauch wird nur bei direkt eingetragenen Komponenten auf der obersten Ebene gebucht.

  • Unterbaugruppen werden nicht automatisch in ihre Einzelteile aufgelöst.
  • Die Lagerbuchung erfolgt daher nicht vollständig über alle Fertigungsebenen hinweg.

Die Anforderung ist derzeit nicht mehr relevant, deshalb hat sich das Thema erledigt.

Viele Grüße
Gerhard