Maßanfertigung im WooCommerce-Shop: Wie ich einen Plattenkonfigurator mit smarter Preislogik gebaut habe
Maßanfertigung im WooCommerce-Shop: Wie ich einen Plattenkonfigurator mit smarter Preislogik gebaut habe
Wer Platten, Zuschnitte oder andere Maßanfertigungen online verkaufen möchte, kennt das Problem: Standard-WooCommerce-Produkte reichen dafür einfach nicht aus. Kunden wollen Länge, Breite und Stärke frei wählen, Zuschnitte und Bohrungen hinzufügen – und am Ende soll trotzdem ein sauber kalkulierter Preis im Warenkorb und in der Bestellung landen.
In diesem Beitrag zeige ich, wie ich genau dafür ein eigenes WooCommerce-Plugin gebaut habe:
- Ein Plattenkonfigurator, der mit Variantenpreisen pro m² arbeitet
- Eine Mindestpreis-Regel: alles bis 0,5 m² wird wie 0,5 m² berechnet
- Optional: Ausschnitt (Preis pro cm) und Bohrungen (Preis pro Stück)
- Per-Produkt Min-/Max-Maße für Länge & Breite
- Alle Konfigurationsdaten landen in Warenkorb, Checkout, E-Mail & Bestellung
- Und: Platten mit Kantenlängen über 100 cm können nur abgeholt werden – Versand wird technisch ausgeschlossen.
Die Ausgangssituation: Variable Produkte reichen nicht
Die Basis des Setups ist ein ganz normales variables Produkt in WooCommerce. Jede Variation steht für eine Stärke (z.B. 20 mm, 35 mm, 50 mm) und hat einen Preis pro m².
Das Problem:
- WooCommerce rechnet standardmäßig mit Stückpreisen, nicht mit „Fläche in m²“.
- Der Kunde soll Länge und Breite in cm frei eingeben können.
- Die Fläche muss dynamisch in m² berechnet werden.
- Es soll eine Mindestfläche von 0,5 m² gelten – alles darunter wird wie 0,5 m² berechnet.
- Zusätzliche Optionen wie Ausschnitt und Bohrungen sollen direkt mit in die Kalkulation fließen.
- Im Backend möchte ich pro Produkt steuern können, welche Minimal- und Maximalmaße überhaupt erlaubt sind.
WooCommerce bringt dafür von Haus aus nichts mit. Also: Eigenes Plugin.
Das Herzstück: Preisberechnung pro m²
Die Logik der Preisberechnung sieht im Kern so aus:
- Länge und Breite werden in cm eingegeben.
- Daraus wird die Fläche in m² berechnet:
Fläche_m2 = (Länge_cm × Breite_cm) / 10.000 - Wenn die Fläche ≤ 0,5 m² ist, wird trotzdem 0,5 m² berechnet.
- Die Variation liefert den Preis pro m².
- Dazu kommen optional:
- Ausschnitt (
cm × Preis/cm) - Bohrungen (
Anzahl × Preis/Stück)
- Ausschnitt (
In PHP sieht die Kernlogik vereinfacht so aus:
<?php
$per_m2 = (float) $cart_item['pkf_price_per_m2']; // Preis pro m² aus der gewählten Variation
$L = (float) $cart_item['platten_laenge_cm'];
$B = (float) $cart_item['platten_breite_cm'];
$area_m2 = ($L * $B) / 10000.0;
if ( $area_m2 <= 0.5 ) {
$base_price = $per_m2 * 0.5;
} else {
$base_price = $per_m2 * $area_m2;
}
// Ausschnitt
$cut_cost = 0;
if ( ! empty( $cart_item['pkf_cut_enabled'] ) ) {
$cut_cost = (float) $cart_item['platten_ausschnitt_cm'] * (float) $cart_item['pkf_cut_price_cm'];
}
// Bohrungen
$hole_cost = 0;
if ( ! empty( $cart_item['pkf_hole_enabled'] ) ) {
$hole_cost = (int) $cart_item['pkf_hole_qty'] * (float) $cart_item['pkf_hole_price_each'];
}
// Endpreis für genau dieses Konfigurations-Produkt
$new_price = round( $base_price + $cut_cost + $hole_cost, wc_get_price_decimals() );
$cart_item['data']->set_price( $new_price );
Wichtig war mir: alles, was der Kunde am Konfigurator macht, muss exakt so im Warenkorb landen – ohne Abweichung.
Per-Produkt Min- und Max-Maße – direkt im Backend
Nicht jedes Produkt darf gleich groß werden. Manche Materialien gibt es nur bis 100 cm Breite, andere bis 300 cm Länge.
Deshalb definiere ich mir:
- globale Defaults (z. B. min. 10 cm, max. 300 cm)
- und optional pro Produkt eigene Min-/Max-Werte für Länge & Breite.
Im Backend gibt es dafür eine eigene Metabox im Produkt:
- „Min. Länge (cm)“
- „Min. Breite (cm)“
- „Max. Länge (cm)“
- „Max. Breite (cm)“
Bleiben die Felder leer, greifen die globalen Standardwerte. Wartbarkeit: erledigt.
Frontend: Konfigurator-Felder direkt auf der Produktseite
Auf der Produktseite binde ich den Konfigurator direkt vor dem „In den Warenkorb“-Button ein:
- Eingabefelder für Länge (cm) und Breite (cm)
- optional Ausschnitt (cm)
- optional Bohrungen (Stück)
- eine dynamische Preiszeile, die sich im Browser aktualisiert
Die JavaScript-Seite macht hauptsächlich drei Dinge:
- Sie horcht auf Änderungen der Variation (also der Stärke) und holt sich daraus den Preis pro m².
- Sie berechnet aus Länge/Breite die Fläche – inklusive 0,5 m² Mindestfläche.
- Sie aktualisiert die Preis-Anzeige live, sodass der Kunde immer sieht, was seine Konfiguration kosten wird.
Die wichtige Verbindung: Sobald WooCommerce im Variationsformular ein found_variation-Event schickt, lese ich variation.display_price aus und nutze das als pricePerM2 im JS-Kalkulator. So sind Backend-Preise, Frontend-Anzeige und Warenkorb-Berechnung synchron.
Datenspeicherung: Alles sauber in Warenkorb & Bestellung
Wenn der Kunde auf „In den Warenkorb“ klickt, passiert serverseitig:
- Länge und Breite werden auf die definierten Min-/Max-Werte geklammert.
- Alle relevanten Parameter werden im
$cart_item_datagespeichert:platten_laenge_cmplatten_breite_cmplatten_ausschnitt_cmpkf_hole_qtypkf_price_per_m2(ganz wichtig!)
- Im Warenkorb werden diese Werte als Artikel-Details angezeigt: „Länge: 101 cm“, „Breite: 101 cm“, „Bohrungen: 3 ד usw.
- Beim Erstellen der Bestellung schreibe ich die Daten als Bestellposition-Metadaten, sodass sie in der Bestellung, in den E-Mails und im Backend sichtbar sind.
Das hat zwei große Vorteile:
- Der Kunde sieht genau, was er konfiguriert hat.
- Die Produktion/Lager hat alle Maße direkt in der Bestellung – ohne manuelle Nacharbeit.
Wichtiger Bugfix: Warum WooCommerce plötzlich „reduzierter Preis“ zeigt
Ein spannendes Detail, das mir bei der Entwicklung begegnet ist:
Die Variation hat ja einen Standardpreis pro m², z. B. 107,00 € / m². Wenn der Kunde eine Fläche von 0,98 m² kauft, landet ein Preis von z. B. 104,87 € im Warenkorb.
WooCommerce sieht dann ungefähr Folgendes:
- Regulärer Produktpreis: 107,00 €
- Tatsächlicher Zeilenpreis: 104,87 €
Viele Themes interpretieren das als „Vorheriger Preis / Reduzierter Preis“ und zeigen so etwas an wie:
Vorheriger Preis: 107,00 €
Reduzierter Preis: 104,87 €
Das ist inhaltlich falsch – es gibt keinen Rabatt, sondern nur eine Flächenberechnung.
Die Lösung: Ich setze im Warenkorb die Produktinstanz so um, dass
- der aktuelle Preis,
- der reguläre Preis
- und ein ggf. bestehender Sale-Preis
aufeinander abgestimmt sind:
<?php
$cart_item['data']->set_price( $new_price );
if ( method_exists( $cart_item['data'], 'set_regular_price' ) ) {
$cart_item['data']->set_regular_price( $new_price );
}
if ( method_exists( $cart_item['data'], 'set_sale_price' ) ) {
$cart_item['data']->set_sale_price( '' );
}
Damit verschwindet die falsche „Rabatt“-Darstellung und es gibt einfach einen sauberen Endpreis.
Versandregel: Platten über 100 cm nur zur Abholung
Ein weiterer wichtiger Punkt: Große Platten lassen sich oft nicht sinnvoll versenden (Kosten, Spedition, Risiko). Die Anforderung war:
Sobald Länge oder Breite größer als 100 cm ist, darf der Kunde nur noch Abholung wählen. Versand muss kategorisch ausgeschlossen sein.
Technisch bedeutet das:
- Ich gehe im Hook
woocommerce_package_ratesalle Artikel im Warenkorb durch. - Sobald ich eine Platte finde, bei der
Länge > 100oderBreite > 100ist, greift eine besondere Versandlogik. - Statt der normalen Versandarten liefere ich nur noch eine lokale Abholoption zurück.
Zusätzlich habe ich eine Sicherheitslogik eingebaut: Selbst wenn in WooCommerce keine passende local_pickup-Versandart definiert ist, erzeugt das Plugin programmatisch eine eigene „Abholung vor Ort“-Rate, damit der Checkout nicht hängen bleibt.
So ist sichergestellt:
- Kleine Platten → normales Versandverhalten (DHL, Paket, etc.)
- Große Platten → keine Versandoption, nur Abholung
WooCommerce Blocks & der „Versenden / Abholung“-Toggle
Wer die neuen WooCommerce Checkout Blocks verwendet, kennt den Umschalter im Checkout:
- „Versenden“
- „Abholung“
Serverseitig hatte ich das alles im Griff: Für große Platten gibt es nur noch eine Abhol-Methode, und die Versandmethoden sind weg.
ABER: Die Blocks zeigen trotzdem den Toggle „Versenden“ an – auch wenn technisch keine Versandrate mehr existiert. Das ist verwirrend, weil der Kunde optisch meint, er könne Versand wählen.
Meine Lösung:
- Ich prüfe im PHP, ob im Warenkorb eine „große Platte“ liegt.
- Wenn ja, gebe ich im Footer ein kleines JavaScript aus, das im Checkout-Block den „Versenden“-Button ausblendet, sobald eine große Platte vorhanden ist.
Damit ist das Verhalten aus Kundensicht jetzt konsistent:
- Bei normalen Produkten: Versenden oder Abholung
- Bei großen Platten: nur Abholung, Versand visuell und technisch ausgeschlossen
Fazit: Maßarbeit für maßgefertigte Produkte
Mit diesem Plugin habe ich mir ein Werkzeug gebaut, das genau zu einem sehr speziellen Anwendungsfall passt:
- Verkauf maßgefertigter Platten mit frei wählbaren Abmessungen
- saubere Preislogik pro m² inkl. Mindestfläche
- zusätzliche Optionen wie Ausschnitt und Bohrungen
- vollständige Transparenz in Warenkorb, Bestellung & E-Mails
- klare Versandlogik für große Platten (nur Abholung)
- und eine Frontend-Erfahrung, bei der der Kunde immer weiß, was er konfiguriert und was es ihn kostet.
Das Ganze zeigt sehr schön: WooCommerce ist eine extrem flexible Basis – aber für spezielle Geschäftsmodelle braucht es oft ein bisschen maßgeschneiderte Entwicklung, um aus „Standard-Shop“ eine wirklich passende Lösung zu machen.
Wenn du selbst etwas Ähnliches planst – etwa für Zuschnitte, Bleche, Holzplatten oder andere Maßeinheiten – lässt sich dieses Konzept relativ gut auf andere Szenarien übertragen: statt m² könnte es genauso gut um Meter, Gewicht oder Volumen gehen. Entscheidend ist immer: Die Logik gehört nah an das Produkt – und muss von Anfang bis Ende konsistent bleiben.