Blog: Latest Entries (15):



Klipper: Ender 3 Bed-Mesh

Da ich klammern nutze, um das Glas-Bed zu befestigen gibt es in der y-Ausrichtung einen Puffer am Rand. In der x-Ache muss natürlich der Offset des CR-Touch mit bedacht werden.


[bed_mesh]
speed: 120
horizontal_move_z: 5
mesh_min: 15,15
mesh_max: 180,200
probe_count: 5,5
algorithm: bicubic
fade_end: 10
fade_target: 0


Zum ausmessen des Mesh den Sensor mittig platzieren und bed_level ausführen.

Klipper: Ender 3 und der Creality Filament-Sensor

Den Filament-Sensor von Creality an einen Ender 3 mit Board Version 4.2.7 anzuschließen ist gerade mit dem Sonic Pad sehr sehr einfach. Man muss nur den Pin heraus bekommen, der zum Glück im Board-Diagramm gut ablesbar ist.

bbcode-image


In der printer.cfg sieht es dann so aus:


[filament_switch_sensor RunoutSensor]
pause_on_runout: False #PAUSE is handled by macro
runout_gcode: PAUSE
insert_gcode: RESUME
switch_pin: PA4

Shopware 6: Eigene Snippet-Sets

Wenn man eigne Snippet-Sets anlegen möchte, bekommt man eine Auswahl an Base-Files angezeigt. Normal sind es die messages.de-DE und die messages.en-GB. Was aber wenn man eine eigene Sprache benötigt, die nicht da und auch nicht im Language-Pack definiert ist? Hier gilt Convention-over-Configuration. Die Snippet-Datei muss einfach nur auf eine bestimmte Art und Weise benannt sein, um als Base-File erkannt zu werden.


src/Resources/snippet/{subfolder}/messages.{locale}.base.json


Nun kann man ein Snippet-Set mit entsprechender Base-File und passender Locale anlegen.

Shopware 6: Cart Collector und Processor

Es gibt zwei Komponenten wenn es um die Anpassung oder die Änderungen am Cart oder seiner Items geht. Die Namen sind aber nicht immer klar in der Bedeutung der verschiedenen Schritte.

Collector: Man sammelt hier nicht die zu ändernden Cart-Items sondern die Daten, die für die Änderungen benötigt werden. Also Datenbank, API-Abfragen und Berechnungen gehören hier rein.

Processor: Hier werden die im ersten Schritt gesammelten Daten auf die Cart-Items angewendet. Auch zusätzliche Items hinzufügen sollte hier geschehen. Wichtig ist natürlich eine Prüfung, ob es diese Items schon gibt.

Der Collector wird einmal ausgeführt. Der Processor kann mehr mals ausgeführt werden, abhängig davon wie viele Änderungen so geschehen.

Was passiert wenn man die Berechnungen nicht im Collector macht sondern im Processor aufgrund der vorhandenen Daten in den Items? Spannender Effekt, der einen echt Zeit kosten kann um hinter das Problem zu kommen.

Ein Rabat von 5EUR in pseudo-Code:

item.price = item.price - 5.00;


Nun wird der Processor 6mal ausgeführt. Ein netter Rabatt von 30EUR ist die Folge.

Richtig wäre:

// collector
data[item.id] = item.price - 5.00;

//processor
if(data[item.id]) {
item.price = data[item.id];
}


Im Collector sammelt man also im 1. Schritt alle neuen Daten zusammen und setzt diese im 2. Schritt im Processor an den richtigen Stellen (wenn nötig auch mehrmals).

Shopware 6: Tipp aktueller Benutzer in der Administration

Benutzer abhängige Ansichten oder Aktionen sind in der Shopware 6 Administration sehr selten. Aber gerade auf dem Dashboard kann toll Ansichten und Nachrichten unterbringen, die speziell für einen Benutzer oder eine Benutzergruppe gedacht sind. Aber dafür muss man wissen wer gerade eingeloggt ist, um z.B. ein Criteria mit den richtigen Filtern zu versehen.


const { currentUser } = Shopware.State.get('session');
console.log(currentUser);


Und dann kann man ohne Probleme CustomFields des Users auslesen.

Shopware 6: Übersetzungen bei Flow-Builder Triggern

Ich weiß, dass als ich die Namen der Trigger zum ersten mal benutzt hatte, ich keine Übersetzungen brauchte und auch etwas verwundert war, dass es einfach so ging. Hat sich nun wohl geändert und es läuft alles nach dem Schema sw-flow.triggers.XXXX.

bbcode-image


Also checkout.order.export.success spaltet sich auf in:

sw-flow.triggers.checkout
sw-flow.triggers.order
sw-flow.triggers.export
sw-flow.triggers.success

cow-warpround wird dabei zu cowWarparound.

Man muss also teilweise ein Adminstration-Module nur für die Snippets anlegen, wenn man ein oder mehrere dieser Flow-Events nutzen möchte.

Shopware 6: Domain-Änderung wurde festegesellt

Diese Meldung muss nicht wirklich was großes bedeuten, denn es kann sein, dass beim Go-Live einfach erst HTTPS aktiviert wurde und vorher nur HTTP da war.

Die APP_URL ist einmal in der .env zu finden und dann nochmal in der Datenbank in der Table system_config mit dem key core.app.shopId. Dort liegt ein JSON-Object, das auch nochmal die APP_URL enthalten kann.

Diesen Wert zu ändern hat geholfen. Wenn man komplett die Domain wechselt und noch eine Stage-Domain für den Shop bei Shopware einträgt, ist es natürlich etwas anderes.. aber für HTTP/HTTPS Probleme sollte es so ausreichen.

PHPUnit und Gitlab-CI

Ein einfacher Weg PHPUnit in einer GitLab-CI Pipeline zu nutzen. An PHP 8.2 arbeite ich noch. Da gab es Probleme mit der Socket Extension. AMQP Extension lief nach einigem Suchen im Internet.


unittest:
image: php:8.1-cli-alpine
stage: test
only:
- branches
script:
- apk add --no-cache rabbitmq-c-dev
- mkdir -p /usr/src/php/ext/amqp
- curl -fsSL https://pecl.php.net/get/amqp | tar xvz -C "/usr/src/php/ext/amqp" --strip 1
- docker-php-ext-install amqp
- docker-php-ext-install sockets
- wget https://getcomposer.org/composer.phar
- php ./composer.phar install
- ./vendor/bin/phpunit

Shopware 6: Lieferschein und Datumsformate

Wenn man den Lieferschein erzeugt, fällt manchmal auf dass das eine Datum sich im Format stark unterscheidet. Es wäre so als würde es eine andere Locale als die anderen benutzen.. oder gar keine.

Der Fehler liegt direkt in der delivery_note.html.twig:

% block document_side_info_contents %}
{{ parent() }}
<tr><td>{% trans with {'%deliveryDate%': config.custom.deliveryDate|format_date('medium', locale=order.language.locale.code)} %}document.deliveryDate{% endtrans %}</td></tr>
{% endblock %}
[code]

Wenn man sich die anderen Datum-Formate in der base oder letter_header Datei anguckt fällt auf, dass hier das Locale nicht aus der Order stammt bzw nicht daraus gelesen wird. Korrekt wäre hier also:

[code]
% block document_side_info_contents %}
{{ parent() }}
<tr><td>{% trans with {'%deliveryDate%': config.custom.deliveryDate|format_date('medium', locale=locale)} %}document.deliveryDate{% endtrans %}</td></tr>
{% endblock %}



Shopware 6: Tipp - Aktive Kategorie

Nach dem ich einmal gesehen habe, dass jemand aus dem Meta-Informationen von page sich per über den Meta-Title den Namen der aktuellen Kategorie geholt hat und dieser nur der Name ist solange kein SEO-Title vergeben wurde, hier ein mal der korrekte Weg:


{{ page.header.navigation.active.name }}


Etwas versteckt, aber da findet man die Category-Entity und darin den Namen (natürlich auch mit entsprechender Übersetzung).

Eine Einzelhandels-Weihnachtsgeschichte

Eine Einzelhandels-Weihnachtsgeschichte
von Miki D. Bork

Es ist ungefähr 16:30. Die Sonne ist schon komplett untergegangen. Beladen mit viel zu viel Tüten, die auch alle schon viel zu voll sind, hetzt man durch den vorweihnachtlichen Matsch. Die Straßenlaternen spiegeln sich leicht in den nassen Steinen der Fußgängerzone, dann sie werden bei weiten von den grellen Schaufenstern der Geschäfte überstrahlt. Schaufenster säumen den Weg zu den Eingängen der Geschäfte, die nicht direkt an die Einkaufsstraße anschließen sondern weiter nach hinten versetzt sind. In diesen Tunneln, die einen von allen Seiten den Besucher mit voller Gewalt in gleisenden Licht die Vielfalt des Angebots in die Augen hämmern stehen angelehnt an die Scheiben erschöpfte Menschen. Kinder laufen in die Geschäfte und lassen die von allen Eindrücken übersättigten Menschen für einen Moment eine Art von Ruhe erfahren, die eher ein Kapitulieren vor der Gesamtsituation ist. Wo kommen wir her? Wo gehen wir hin? Was ist der Sinn des Lebens? Alle diese Fragen verlieren gerade an Bedeutung. In welchen überfüllten Kaufhaus ist das nächste Klo? Hätte man den langen Weg zum Parkhaus doch antreten sollen, um ein paar der Einkäufe im Auto abladen zu können? Wie lange hat dieses verdammte Parkhaus überhaupt noch auf? Länger als 18:00? Dann grummelt der Bauch. Getrunken hat man auch länger nichts und eine Glasflasche mit Wasser mitzuschleppen, wäre auch zu schwer. Der Schneematsch nähert sich mit jedem einzelnen Kunden immer weiter den automatischen Schiebetüren des Geschäfts.
Während die Kinder in der Spielzeugabteilung die großen Regaler mit den verschiedenen Spielzeugserien begutachten versucht der Partner unbemerkt was zu kaufen. Wenn das Bargeld reicht. Für was zu essen wird es nicht mehr reichen und man muss in eine Bank, um neues Geld zu holen. Vorher noch Kontoauszüge um zu gucken ob man noch im Budget liegt.
Die McDonalds-Filiale platz aus allen Nähten. Die obligatorischen Pizzarien sind auch voll. Unter jeden Tisch stapeln sich die Einkaufstaschen. Das Plastik ist die kleine Grenze zwischen den wertvollen Waren und dem unbändigen Kräften der Natur, der sich als Schneematsch nun auch schon der Pizzaria und den Innenraum der Schuhe der Gäste bemächtigt hat. Das gedämmte Licht und der warme Geruch der Pizza könnte einen nun fast einnicken lassen, wenn nicht diese extreme Lautstärke herrschen würde. Am Nachbartisch verhindert eine Mutter mit viel Geschrei gerade so, dass das Kind auf der Suche nach seinem Einkauf die Geschenke-Tüte öffnet. Der Bruder liest konzentriert in der Anleitung seines neuen Computerspiels. FSK? +-4 Jahre sind sicher ok, solange es nicht FSK18 ist, ist es sicher ok. Zwei Monate später wird er glücklich sein, sich das Spiel gekauft haben, weil es dann doch auf dem Index gelandet ist.
Am Ende steht man im noch kalten in einer endlosen Schlange in Richtung Ausfahrt im Parkhaus und wartet, dass man endlich dort bezahlen und in die Freiheit entlassen werden kann. Sicher versucht gerade jemand das Kleingeld, das vom Weihnachtseinkauf sich in rauen Mengen angesammelt hat, an der mit einem Menschen besetzen Parkhauskasse los zu werden. Eine Münze fällt dabei herunter.
Der Beifahrer befreit sich aus Gurt, Tüten und quetscht sich aus der Tür. Tritt in noch mehr Matsch. Hätte man sich es also sparen können die Schuhe im Parkhaus noch mal sauber zu machen, bevor man ins Auto stieg. Alle denken sich: Scheiß auf die 2 Mark.. aber es sind 2 Mark. Die Uhr interessiert es nicht und sie rückt un erbärmlich weiter und macht den Kindern klar, dass diesen Samstag genau die TV-Serie die letzte Woche mit einer Doppelfolge aufwartete heute nicht zu sehen geben wird. Showview wäre der Retter in der Not gewesen, aber mit dieser endlosen Schlange an Autos, die sich bis auf die oberste Ebene des Parkhauses erstreckt, hatte niemand gerechnet. Wie auch? Eine Frage, die man auf das nächste Jahr
verschiebt, wenn man wieder in dieser Situation ist. Du schließt genervt deine Augen. Dieses CBD-Schlafspray macht echt intensive Träume. Leider sehr teuer, aber ein paar hatten sowie noch gefragt, was du dir zu Weihnachten wünscht. Praktisch. Du musst denen nur noch kurz den Link schicken.

Shopware 6: Ein CSV Download in der Administration

Daten per Criteria abfragen und in einer Table oder einer Form darzustellen oder verarbeiten ist in der Shopware 6 Administration extrem einfach und fast zu 100% genau so wie in PHP. Aber manchmal will man auch auf eine sehr spezielle Art oder Weise Daten erhalten und diese dem Benutzer direkt als Download anbieten können.
Hier ein kleines Beispiel eines CSV-Downloads der von einem API-Controller mit der Route "/api/example-module/download/csv/{id}" kommt. Man kann aber genau so gut alles per Criteria abfragen und das CSV in JavaScript bauen.

Die Methode für den Download sieht mit Controller-Lösung so aus:

downloadCsv(item) {
const httpClient = Shopware.Application.getContainer('init').httpClient;
httpClient.get('/example-module/download/csv/{' + item.id).then(result => {

const pom = document.createElement('a');
pom.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURIComponent(result.data));
pom.setAttribute('download', 'csv_' + item.id + '.csv');

if (document.createEvent) {
const event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
pom.dispatchEvent(event);
}
else {
pom.click();
}
});
}


Das Prinzip ist an sich sehr einfach. Man baut sich ein a-Element mit gesetzten download-Attribute und simuliert einen Click darauf. Nur hinterlegt man hier die Daten als Data-URL und nicht die URL zum Controller direkt. Das würde nicht funktionieren als dann dort die Authorization-Infos im GET-Header fehlen würden.

Die Data-URL kann eben aus Daten von einem Controller gebaut werden oder direkt aus Daten, die JavaScript aggregiert wurden.

Sehr viel einfacher als die alten Lösungen mit FileSaver-Lib und viel hin und her wandeln von Blobs.

Older posts:

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