Blog: Latest Entries (15):


Shopware 6: Administration Built Error

Wenn man solch einen Fehler erhält


npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! administration@1.0.0 build: `mode=production webpack`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the administration@1.0.0 build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR! /var/www/.npm/_logs/2022-11-24T11_18_35_338Z-debug.log


sollte man gucken, ob vorher ein Fehler dieser Art aufgetreten ist


Error: EACCES: permission denied, ...

Reisen: WIFIonICE mit Linux nutzen

Während die Intenetnutzung im ICE mit dem Smartphone ohne Probleme geht, kann es mit Linux schnell zu Problemen kommen. Das liegt am IP-Bereich und Docker. Zwar kann sich das Gerät mit Linux ohne Probleme mit dem eigentlichen Netzwerk verbinden, aber dann kann die Seite zum bestätigen der AGBs nicht geladen werden.

Anleitung:
- Mit dem WLAN verbinden
- prüfen welcher IP-Bereich zu gewiesen wurde
-
ip addr

- gucken welcher Eintrag mit dem Bereich kolliedert
- diesen Eintrag entfernen
-
sudo ip link delete <name>


Bei mir war es der Bereich 172.18.

bbcode-image

Shopware 6: Tipp Media::fileName ändern

Meine erste Lösung war es den MD5-Hash als Dateiname zu nehmen. Dann später kam wollte ich doch lieber ein CustomField dafür nutzen (wie ich auch schon in der Shopware 5 Version ein Attribute genutzt hatte). Funktionierte alles super bis ich dann auf ein System kam, das nicht im dev-Mode lief. Am fileName hängt wohl mehr als man denkt und ich habe gelernt, dass man in einem produktiven Shopware 6 nicht den fileName ändern sollte. Kommt mit auf meine Liste der ganz großen Shopware 6 Nein-Nein's.

bbcode-image

Ender 3: Creality Spider Fan Duct

Eine wirklich gutes Upgrade für den Creality Ender 3 ist das Spider Hotend des selben Herstellers. Es gibt auch eine V2.0, die von der Größe besser passen sollte, als Ersatz des Stock-Hotends. Aber die 1.0 Version ist auch schon sehr gut und bedeutend günstiger.

bbcode-image


Mit dem Hotend ist PETG Druck bei 100-120mm/s bei 245 Grad kein Problem mehr mit dem Ender 3 (Pro). Da das Hotend aber insgesamt höher ist würde der Lüfter nun gegen den Heizblock blasen und nicht mehr aus das frischgedruckte Filament. Es gibt bei Thingiverse Ersatz, der aber bei mir am Ende zu dicht über dem Filament und auch zu dicht an der Nozzle war. Deswegen habe ich dieses Design etwas angepasst. Gedruckt mit Resin (weil der Ender 3 ja gerade im Umbau war). Funktioniert super und auch wenn man etwas Filament vom Bed im ersten Layer abstehen zieht es nicht damit direkt den gesamten Layer wieder ab.

bbcode-image


Mein Designs dafür bei Tinkercad

Shopware 6: Bestellstatus ändern ohne Mail zu verschicken

Oft will man einfach nur den Status der Stellung anpassen, weil irgendwas mit der Bestellung nicht stimmt oder man sie auf abgeschlossen setzen will, aber der Rest über das ERP/die WaWi läuft. Man will keine Mail an den Kunden senden. Leider ist die Mail immer direkt aktiviert und man muss immer daran denken diese zu deaktivieren.

Nervig.. also ein tolles kleines (wirklich kleines) Plugin-Projekt:

https://gitlab.com/hpries/HPrSendMailStateChange

normal
bbcode-image


jetzt
bbcode-image

JavaScript: Detect scroll to bottom

Um einen einfachen kleinen Dialog zum Akzeptieren von AGBs/T&Cs/etc braucht man nur relativ wenig JavaScript Code und auch kein JQuery mehr.


<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
crossorigin="anonymous">
<style>
#reader {
max-height: 200px;
border: 1px solid lightgray;
border-radius: 3px;
overflow: auto;
padding: 0.25rem;
}

#page {
display: flex;
justify-content: center;
padding-top: 5rem;
}
</style>
</head>
<body><div id="page">

<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Scroll to Accept</h5>
<p class="card-text">
<div id="reader">
Gott zum Gruße! Das feist Weichteile. Die geflissentlich
Begrüßungsgeld meucheln. Der töricht Lump grämen. Das bräsig Schürzenjäger.
Der grobschlächtig Edelmut. Der Kummerspeck anschwärzen der bierernst Lump.
Fatzke und Panzerkampfwagen flanieren pompös Schindluder. Die pomadig Bagage
foppen. Narr und Affenzahn schlampampen feist Quasselstrippe. Presssack und
Kuppelei picheln altbacken Kummerspeck. Die geflissentlich Luder frickeln.
Das Begrüßungsgeld schlampampen die gebeutelt Flegel. Habe die Ehre!
</div>
</p>
<button id="ok" class="btn btn-primary" disabled type="button">Accept</button>
</div>
</div>
<script>
document.getElementById('reader').addEventListener('scroll',
(e) => {
const element = e.target;
if(element.offsetHeight + element.scrollTop >= element.scrollHeight) {
document.getElementById('ok').removeAttribute('disabled');
}
}
, true)
</script>
</div>
</body>
</html>

Shopware 6: Storefront Plugins

Ein JavaScript Plugin für die Shopware 6 Storefront zu schreiben ist an sich sehr einfach.
Das Plugin an sich ist sehr einfach strukturiert.

example-plugin/index.js

import Plugin from 'src/plugin-system/plugin.class';

export default class ExamplePlugin extends Plugin {
init() {
console.log('plugin is bind to this element', this.el);
}
}


Die Klasse registiert man in der main.js:


import ExamplePlugin from './example-plugin/';

window.PluginManager.register('ExamplePlugin', ExamplePlugin, '[data-example-plugin]');


Nun muss man das nur noch an ein HTML-Element binden, so dass es wenn dieses Element geladen wird auch gestartet wird.


...
<div data-example-plugin="true"></div>
...


Im Plugin sucht man sich dann alle benötigten Elemente und registriert dort alle benötigten Listener.

Ausführlich steht alles hier: https://developer.shopware.com/docs/guides/plugins/plugins/storefront/add-custom-javascript

Wobei ich eher diese Seite https://mrtnschndlr.medium.com/die-neue-shopware-6-storefront-und-was-du-alles-dar%C3%BCber-wissen-solltest-e1e5621b6397 empfehlen würde.

die Art und Weise erinnert mich etwas wie ich damals bei meinen cJSv2-Framework die Controller an Elemente gebunden und initialisiert habe.. der default-Name der Init-Methode war da auch init.

Der DomAccess-Helper ist super und sollte man so verwenden. Über den kann man auch den QuerySelector nutzen und dass schützt hoffentlich davor, dass ein Haufen klassischer Webdesigner versuchen JQuery wieder da rein zu bringen. Der HttpClient ist auch nett aber man kann genau so gut direkt mit fetch() arbeiten, wenn man nicht unbedingt irgendwelche Steinzeit Browser supporten möchte. Aber besonder mag ich von den Helpern DeviceDetection.isTouchDevice() weil man damit einfach unterschieden kann ob man gerade ein Touch oder ein MouseEnter
Event nutzen muss.

Shopware 6: Lesbares Javascript

Wenn man in der Storefront an einem eignem JavaScript-Plugin arbeitet ist es beim Debuggen, oft nervig, dass der JavaScript Code nicht direkt lesbar ist. Oft weiß man beim eigenen Code direkt was los ist, aber gerade wenn 3rd Party Plugins auch mit rein spielen, kommt man nicht drum herum sich Exceptions mit lesbaren Stacktrace anzeigen zu lassen.

Das ist zum Glück extrem einfach:

* Port 9998 aus dem Docker-Container durchleiten
* Dem SalesChannel in dem man entwickelt die Domain "localhost" zu ordnen
* das Script bin/watch-storefront.sh starten

Nun kann man localhost:9998 im Browser aufrufen. Der JavaScript Code ist lesbar und Änderungen werden sofort übernommen ohne dass man die Storefront neu bauen muss.

Shopware 6: Produkt-Analyse per SQL

Einmal schnell gucken, wie viele und welche Art von Produkten über welchen SalesChannel und URL zu kaufen sind.


SELECT x.url, 'single' type, count(x.id)
FROM (
select d.url, p.id, p.parent_id, count(p2.id) children from sales_channel_domain d
join sales_channel sc on d.sales_channel_id = sc.id
join product_visibility pv on pv.sales_channel_id = sc.id
join product p on p.id = pv.product_id
left outer join product p2 on p.id = p2.parent_id
where sc.active = 1
group by d.url, p.id, p.parent_id
)x
WHERE x.parent_id is null and x.children < 1
group by x.url

union all

SELECT x.url, 'main' type, count(x.id)
FROM (
select d.url, p.id, p.parent_id, count(p2.id) children from sales_channel_domain d
join sales_channel sc on d.sales_channel_id = sc.id
join product_visibility pv on pv.sales_channel_id = sc.id
join product p on p.id = pv.product_id
left outer join product p2 on p.id = p2.parent_id
where sc.active = 1
group by d.url, p.id, p.parent_id
)x
WHERE x.parent_id is null and x.children > 0
group by x.url

union all

SELECT x.url, 'variant' type, count(x.id)
FROM (
select d.url, p2.id, p2.parent_id, 0 children from sales_channel_domain d
join sales_channel sc on d.sales_channel_id = sc.id
join product_visibility pv on pv.sales_channel_id = sc.id
join product p on p.id = pv.product_id
left outer join product p2 on p.id = p2.parent_id
where sc.active = 1
)x
group by x.url


Shopware 6: Id und die DBAL-Connection

Gerade in Migrations kommt man manchmal in darum herum mit den UUIDs arbeiten zu müssen. Da es binary-Daten sind muss man etwas mit den anstellen, um die aus Shopware 6 bekannte Darstellung zu erreichen und auch um diese wieder die in Datenbank zu bekommen.

Lesen der Id:
Um die aus Shopware 6 gewohnte Darstellung zu bekommen muss man in MySQL die HEX-Function verwenden. Zusätzlich muss der String noch in Kleinbuchstaben umgewandelt werden.

Schreiben der Id:
Zuerst wieder alles in Großbuchstaben umwandelnt. Dann muss mit der MySQL-Function UNHEX der Hex-String wieder in binary Data umgewandelt werden.

Es bleibt zwar etwas umständlich, aber ist damit durch aus handhabbar. Da es in dem Sinne kein Autoincrement gibt für die UUIDs hilft dann dort Uuid::randomHex().


Shopware 6: Was schon alles damit entwickelt wurde

Ich arbeite nun doch schon einige Zeit mit Shopware 6. Während ich am Anfang vielen Konzepten etwas kritisch gegenüberstand, bin ich nun doch sehr von fast allen Dingen überzeugt. Ich habe viele verschiedene Dinge schon mit Shopware 6 realisiert und einiges wäre in Shopware 5 nicht so einfach gewesen.
Gerade Vue.js in der Administration ohne irgendwelche zusätzlichen Lizenzen nutzen zu können ist super. Ich mochte immer Vue + Bootstrap und Symfony. Also am Ende fühle ich mich so was von extrem in meinen Vorlieben bestätigt... wenn auch Shopware diese Kombinationen nutzt muss ich ja schon immer richtig gelegen haben :-)

Varianten sind immer noch viel zu kompliziert. Daten in das Model für Emails rein zubekommen ist wirklich viel zu umständlich. N:M Relation in DAL ist umständlich bzw wie früher mit puren SQL. Entweder alles vorher löschen oder sich merken was genutzt wird und alles was nicht dazugehört löschen. Aber am Ende kommt man ja gut damit klar... ist eben wie mit JDBC oder PDO direkt zu arbeiten und dass habe ich lange genug gemacht.

So.. aber was kann man alles mit Shopware 6 so alles bauen? Ich habe bis jetzt nur für Kunden direkt entwickelt und falls jemand etwas hier von gerne hätte, geht das leider nur über Anfrage und dann wird ein Angebot erstellt.

1. Adressänderungen verhindern
Wie schon bei Shopware 5 war es nötig, weil SAP sonst überfordert ist.

bbcode-image


2. Register-Form erweitern
Das zu Erweitern war am Ende sehr viel einfacher als gedacht und ich nutzt einfach das Data-Mapping Event für den Customer. Der richtige Weg? Für mich funktioniert er gut.

bbcode-image


3. Blog Einträge
Das kostenlose Blog-Plugin ist schon echt super. Es fehlt nur ein Flag um Posts von er Suche auszuschließen, Typen, Rechte über Rules und Datei-Anhänge. Ja.. ich weiß.. ich könnte mich da beteiligen und alles einbauen.. sollte ich wirklich machen. Aber bis jetzt war nur Zeit das Plugin zu erweitern um Rezepte aus einem Panipro-System zu importieren.

bbcode-image


Typen sind natürlich dynamisch und es werden nur Typen auf der linken Seite angezeigt, die auch gefunden wurden.

4. Bonuspunkte und passende Produkte
Produkte die man nur oder auch gegen Bonuspunkte kaufen kann. Das war etwas komplexer und man musste über Collector und Processor des Carts eingreifen. Heute würde ich wohl einiges ein wenig anders machen, aber nicht viel und auch nur minimale Änderungen.

bbcode-image


5. Konfigurator Style #1
So kann man sich z.B. Geschenkkörbe oder PCs/Notebooks konfigurieren. In der Administration kann man sich ein Config-Preset anlegen und es verschiedenen Products zuordnen.

bbcode-image


6. Konfigurator Style #2
Ein Produkt das z.B. Hackfleisch beinhaltet so konfigurieren in welchen Formen man das Hack gerne geliefert bekommen würde. Wurde als Teil von Punkt 7 entwickelt.

bbcode-image


7. Paket verkauf
Erst alle Pakete verkaufen und dann erst das Tier schlachten. Spart Lagerkosten und andere Aufwände. Hier habe ich gelernt warum Varianten noch immer umständlich sind und CustomFields zu syncen zwischen Varianten schlechter ist als eine eigene Entity dafür zu erstellen. Aber die Anforderungen waren zuerst so das CustomFields die einfacher Lösung waren.

In Aktion:
https://www.mein-elbtaler-shop.de/Bio-Rind-Demeter/ME-1130.1

Wem also etwas hier von gefallen hat, kann sich gerne melden. Wir freuen uns immer über Anfragen.

Shopware 6: Hauptvariante ändern

Etwas was bei Shopware 6 so komplett fehlt und wo ich schon mehrmals gehört habe, dass es Kunden/Benutzern Probleme macht, ist die Hauptvariante bei Shopware 6. Die Hauptvariante ist immer die Variante, die im Dialog beim Erzeugen der Varianten zuerst gewählt wurde. Es gibt danach keine Möglichkeit mehr es zu ändern.

An sich ist die Hauptvariante aber auch nur ein FK am Product. Man kann dort einfach eine andere Variante eintragen und schon hat man seine neue Hauptvariante. Also alles auf DB-Ebene sehr einfach.

Auch das in die Administration einbauen war dann relativ einfach. Man brauchte nur zusätzlich ein Repository und eine Criteria für Varianten (WHERE parent_id = ... ). Die Id kann man direkt per v-model setzen am Product.

https://gitlab.com/hpries/HPrMainVariant

Dort habe ich meine Implementierung einmal hinterlegt. Darf gerne verwendet werden, wenn jemand oder ein Kunde auch dieses Problem hat.

bbcode-image

Shopware 6: Non-Stackable Cart-Items

Gerade wenn man ein Produkt konfigurieren kann, ist es wichtig, dass CartItems nicht einfach aufaddiert werden, sondern jede Konfiguration als eigenes CartItem im Warenkorb abgelegt wird. Das CartItem von Shopware 6 hat auch ein stackable-Flag. Nun könnte man glauben, wenn man dieses auf false setzt, dass nicht das vorhandene CartItem geändert sondern ein neues angelegt wird, wenn erkannt wird, dass das vorhandene nicht stackable ist. Falsch! Man bekommt eine Exception.

Die Lösung ist zum Glück sehr einfach. Man muss selbst die Id des CartItems ändern um ein neues anzulegen. Also wenn man setStackable(false) setzt auch gleich die Id neu setzen. Oder man baut sich ein allgemeines Plugin, dass es macht.

BeforeLineItemAddedEvent:

if (!$event->getLineItem()->isStackable()) {
$event->getLineItem()->setId(Uuid::randomHex());
}

Older posts:

Möchtest Du AdSense-Werbung erlauben und mir damit helfen die laufenden Kosten des Blogs tragen zu können?