Client-Scripts - Tabellen auswerten
By Lars Mäurer on 8. Februar 2023
IntermediateClient-Scripts: Tabellen auswerten
Einleitung:
In diesem Artikel wird beschrieben, wie man mit Client-Scripts Eingaben des Nutzers aufnehmen und auswerten kann.
Insbesondere können auch ganze Tabellen, die der Nutzer befüllt, ausgewertet werden (!).
Ich benutze hier als Beispiel einen entsprechenden realen Fall – dabei geht es um Getränkekästen bzw. -paletten.
Vorüberlegungen:
(1) Jeder Artikel enthält ein Feld, in welchem beim Anlegen des Artikels schon ausgewählt wird, um welche Kastensorte es sich handelt. Es gibt unterschiedlich große Kästen. Für jeden Artikel ist also bekannt, um welche Kasten-Sorte es sich handelt.
Die Aufgabe besteht darin, die komplette Bestellung dahingehend auszuwerten, ob mit den verschiedenen Kasten-Sorten komplette Paletten gebildet werden können. Dazu muss die Summe aller Kästen einer Sorte einen bestimmten Wert annehmen. Im realen Fall also entweder 40/80/120/... oder 60/120/180/... Kästen pro Palette, je nach Größe der Kästen. Die Kasten-Sorten sollen auf einer Palette nicht gemischt werden.
(2) Die Bestell-Posten werden – wie üblich – in eine Tabelle in der Einkaufsrechnung (Purchase Order) eingetragen. Wir benötigen nun ein Client Script, welches die Bestellung des Nutzers darauf untersucht, ob die Paletten vollständig sind oder nicht.
(3) Wichtig: Jede Zeile der Child Table ("Items"), die in der Einkaufsrechnung befüllt wird, gehört zum Doctype "Purchase Order Item". In diesem Doctype muss über "fetch from" die jeweilige Kastensorte der bestellten Artikel ermittelt werden.
Hier gibt es also zwei Felder für den Artikel und die entsprechende Kastenvariante.
Da der Nutzer sowieso für jede Zeile als Erstes den Artikel (Item) auswählt (damit ist also der Artikel (Item) schon bekannt), ist dies nicht schwierig:
Eingabe im Feld "Kastensorte".
Das entsprechende Feld in "Purchase Order Item" heißt in diesem Fall "Kastensorte", im Artikel selbst (Item) heißt es "Kasten Variante".
Dies wird benötigt, damit man in dem Client Script, welches auf Purchase Order läuft, diese Angaben über die Kastensorte auch ansprechen kann.
Kommen wir nun zum Client Script zum Doctype Purchase Order:
frappe.ui.form.on('Purchase Order', {validate: function(frm) {// hier werden die beiden Variablen definiert, die für's Zusammenzählen benötigt werden.var summe40 = 0;var summe60 = 0;// das ist die Schleife, in der alle Zeilen der Items-Tabelle durchgegangen werden,// um die Anzahl der Kästen für die beiden Paletten-Arten zu ermitteln:for (var row in frm.doc.items){if (frm.doc.items[row].kastensorte == "B (20x0.5L) (40)") {summe40 += frm.doc.items[row].qty}if (frm.doc.items[row].kastensorte == "A (9x1L) (60)") {summe60 += frm.doc.items[row].qty}}// Jetzt sind die beiden Summen "summe40" und "summe60" für die komplette Bestellung bekannt!// und hier werden dann die verschiedenen Fälle abgehandelt... entweder Fehlermeldungen ("throw)" oder Textausgabe ("msgprint") :if ((summe40 / 40) + (summe60 / 60) < 2){frappe.throw("Fehler.<br>Es müssen mindestens 2 Paletten vollständig befüllt sein.<br>Diese Mindestanzahl vollständig befüllter Paletten wurde nicht erreicht.")}if (summe60 % 60 >= 30){frappe.throw("Fehler.<br>Kasten-Variante A (9 x 1L) geht nicht auf.<br>Die Anzahl der Kästen muss durch 60 teilbar sein, damit die Paletten vollständig sind.<br>Es werden noch " + (60 - (summe60 % 60)) + " Kästen benötigt.")}if ((summe60 % 60 < 30) && (summe60 % 60 !== 0)){frappe.throw("Fehler.<br>Kasten-Variante A (9 x 1L) geht nicht auf.<br>Die Anzahl der Kästen muss durch 60 teilbar sein, damit die Paletten vollständig sind.<br>Es sind " + (summe60 % 60) + " Kästen zuviel.")}if (summe40 % 40 >= 20){frappe.throw("Fehler.<br>Kasten-Variante B (20 x 0.5L) geht nicht auf.<br>Die Anzahl der Kästen muss durch 40 teilbar sein, damit die Paletten vollständig sind.<br>Es werden noch " + (40 - (summe40 % 40)) + " Kästen benötigt.")}if ((summe40 % 40 < 20) && (summe40 % 40 !== 0)){frappe.throw("Fehler.<br>Kasten-Variante B (20 x 0.5L) geht nicht auf.<br>Die Anzahl der Kästen muss durch 40 teilbar sein, damit die Paletten vollständig sind.<br>Es sind " + (summe40 % 40) + " Kästen zuviel.")}elsefrappe.msgprint("Alles korrekt!<br>Bestellung wird gespeichert.");}}});
Erklärung für "validate":
Zeile 2 "validate:" sorgt dafür, dass das Script beim Drücken auf "Save" durchlaufen wird und nur dann "Save" tatsächlich angenommen wird, wenn kein Fehler mehr auftritt. Die Eingaben werden praktisch vor dem Speichern validiert.
Die for-Schleife ...
for (var row in frm.doc.items){if (frm.doc.items[row].kastensorte == "B (20x0.5L) (40)") {summe40 += frm.doc.items[row].qty}if (frm.doc.items[row].kastensorte == "A (9x1L) (60)") {summe60 += frm.doc.items[row].qty}}
...ist der eigentlich wichtige Teil des Scripts.
for (var row in frm.doc.items)
...geht nacheinander durch alle Zeilen der Tabelle (man muss hier keinen Zähler "i" o.ä. benutzen, javascript kann das auch so) – juhu! :-)
Man beachte, wie in der nächsten Zeile das Element "kastensorte" angesprochen wird:
frm.doc.items[row].kastensorte
Dabei ist [row] sozusagen der Index einer einzelnen Zeile der Tabelle.
Alles wird kleingeschrieben!
Die konkreten Berechnungen, die im Beispiel passieren, sind hier natürlich nicht wirklich wichtig.
Die Anzahlen einer bestimmten Kasten-Sorte werden hier einfach aufaddiert - je nach Anzahl der Kästen pro Zeile ("qty").
Wenn die entscheidende Schleife komplett durchlaufen ist, sind die Summen "summe40" und "summe60" korrekt ausgerechnet und geben an, wie viele Kästen es pro Sorte in der Bestellung gibt.
Es können nun – je nach Ergebnis – verschiedene Fehlermeldungen ("throw") oder normale Textnachrichten ("msgprint") ausgegeben werden.
if (summe60 % 60 >= 30) {frappe.throw("Kasten-Variante A geht nicht auf. Es werden noch " + (60 - (summe60 % 60)) + " Kästen benötigt.");}
Hier wird zum Beispiel getestet, ob die Summe der (kleinen) Kästen durch 60 teilbar ist - dann wäre eine Palette komplett voll.
Das Prozentzeichen "%" wird hier als Operationszeichen benutzt, es rechnet den Rest einer Division aus.
Falls die Anforderung nicht erfüllt ist, wird eine Fehlermeldung ("throw") ausgegeben, in der man auch gleich mitteilen kann, wie viele Kästen noch fehlen.
Das Entscheidende dabei: "Save" wird nur dann ausgelöst, wenn es keine Fehler mehr gibt. In diesem Fall ist das sehr gut und genauso gewollt.
Es passiert de facto genau das gleiche, wie bei vergessenen Pflicht-Feldern. Dann würde nämlich auch eine Fehlermeldung kommen, und "Save" würde nicht ausgelöst werden.
Weitere Artikel zum Buchhaltung