Table of Content
App "Produkt Konfigurator - Restriktionen"
Basis App zur Begrenzung von Auswahlmöglichkeiten
product_configurator_domain_ext
Ausgangslage
Der von der Community bereit gestellte Produkt Konfigurator bietet für die Attributwerte bereits Möglichkeiten zur Datenvalidierung zur Verfügung. Da der Produkt Konfigurator aber für unsere Kunden weitaus flexibler genutzt werden soll, wird eine einfach erweiterbare Möglichkeit für die Datenvalidierung benötigt.
Grundlagen
Diese App bietet eine mehrfach nutzbare Basis zur Implementierung von Restriktionen bei der Auswahl oder Eingabe von Werten, Produkten oder was auch immer. Die App enthält keine für den Endbenutzer sichtbaren Funktionen, wird aber von anderen Apps genutzt.
Folgende Apps nutzen die Funktionen dieser App und können als Beispielimplementationen genutzt werden:
Product Configurator - Input Fields (product_configurator_input)
Konzept
Die App liefert ein Mixin Objekt aus, welches auf andere Objekte angewendet werden kann. Durch dieses Objekt ist es möglich auf den abgeleiteten Objekten über die Formularansicht Python Code zur Definition von Restriktionen sowie eine zugehörige Fehlermeldung zu erfassen. Zudem wird eine Sequenzierung der Einträge durch das Mixin implementiert.
Im Weiteren wird der Produktkonfigurator dahingehend erweitert, dass bei der Validierung der Konfiguratorwerte auch die Restriktionen überprüft werden und wenn nötig die zugehörige Meldung ausgegeben wird.
Implementierung
Mixin Objekt product.config.code.restriction.mixin
Dieses Abstrakte Objekt enthält die benötigten Felder für die Implementierung der Restriktionsverwaltung. Das Mixin kann mittels Inheritance auf bestehende oder neue Objekte angewendet werden:
class ProductConfigInputCodeRestriction(models.Model):
_name = 'product.config.input.code.restriction'
_description = 'product.config.input.code.restriction'
_inherit = 'product.config.code.restriction.mixin'
# ...
Dadurch wird das Objekt um folgende Felder erweitert:
sequence: Integer Felder zur Definition der Reihenfolge in welcher die Restriktionen validiert werden
restriction_code: Textfeld zur Pflege des Python Codes für die Validierung
restriction_message: Textfeld zur Definition der Fehlermeldung für den Benutzer bei negativer Validierung
Mixin Objekt product.config.code.restriction.doc.mixin
Dieses Abstrakte Objekt enthält die benötigten Felder und Methoden zur Generierung der Dokumentation um den Benutzer die Definition von Restriktionen zur Vereinfachen. Das Mixin kann mittels Inheritance auf bestehende oder neue Objekte angewendet werden:
class ProductConfigInput(models.Model):
_name = 'product.config.input'
_description = 'product.config.input'
_inherit = 'product.config.code.restriction.doc.mixin'
# ...
Dadurch wird das Objekt um folgendes Feld erweitert:
restriction_code_doc: Berechnetes HTML Feld welches die Dokumentation für die Pflege der Python Restriktionen enthält
Methode create_restriction_context
Diese Methode definiert den Kontext, welcher beim Evaluieren der Python Restriktion zur Verfügung steht.
Sie liefert ein Dictionary Objekt zurück, welches einen Key session enthält welcher als Wert die komplette Session des aktuellen Konfigurator enthält. Damit die Definition von Restriktionen für den Benutzer einfacher wird, sollten aber zusätzliche Schlüssel zur Verfügung gestellt werden. Dies kann durch die Überladung der Methode gemacht werden:
@api.model
def create_restriction_context(self):
result = super().create_restriction_context()
restriction_values = {}
# fill the restriction_values dictionary with any data thata should be available in restriction code
result['whatever'] = namedtuple('Restrictions', restriction_values.keys())(*restriction_values.values())
return result
Würde als Beispiel folgender Eintrag in restriction_values geschrieben
restriction_values['allowed_product_ids'] = [3,4,5,6,7,8]
so kann im Feld restriction_code mit whatever.allowed_product_ids
auf die entsprechenden Werte zugegriffen werden. Natürlich kann und soll der Schlüssel whatever umbenannt werden.
Methode get_context_doc_data
Damit der Benutzer bei der Pflege der Restriktionen weiss, welche Objekte er im Zugriff hat, generiert diese Methode einen Hilfetext mit den verfügbaren Variablen.
Sie liefert ein Dictionary Objekt zurück, welches einen Key session enthält und als Value die Beschreibung der Variablen. Werden weitere Variablen angeboten, so sollten diese ebenfalls als Dokumentation zur Verfügung gestellt werden. Dies kann durch die Überladung der Methode gemacht werden:
@api.model
def get_context_doc_data(self):
result = super().get_context_doc_data()
result['whatever'] = {'allowed_product_ids': _('List of allowed Product Variants'),}
return result
Der Rückgabewert dieser Methode wird dann in HTML Code umgewandelt und im Feld restriction_code_doc zwischengespeichert. Dieses Feld soll dann unterhalb der Python Code Restriktionen ausgegeben werden.
Methode check_restrictions
Zur Validierung der Restriktionen anhand der Werte im Wizard wird diese Methode aufgerufen.
Sie liefert ein Tuple bestehend aus einem Boolean und einem String zurück. Der Boolean Wert gibt an, ob die Validierung erfolgreich (True) war, oder nicht (False). Der String Parameter enthält die anzuzeigende Fehlermeldung welche ausgegeben wird wenn die Validierung nicht erfolgreich war.
Die Original Methode gibt immer True, '' zurück. Jede Nutzung des Mixin muss also seine eigene Validierungslogik implementieren. Dies kann durch die Überladung der Methode gemacht werden:
@api.multi def check_restrictions(self): result, msg = super().check_restrictions() if not result: return result, msg
# self is not set at the beginning of the wizard so we need to check this and stop validation in this case
if not self: return True, '' ctx = self.create_restriction_context()
for your_data in self.your_model: for restriction in your_data.code_restriction_ids: try: res = safe_eval(restriction.restriction_code, locals_dict=ctx, nocopy=True) except ValueError:
# Maybe not all values are set at the validation time so we ignore this restriction continue if not res: return False, restriction.restriction_message
# No negative validation found so return the result of super call return result, msg
Der Rückgabewert dieser Methode wird dann in HTML Code umgewandelt und im Feld restriction_code_doc zwischengespeichert. Dieses Feld soll dann unterhalb der Python Code Restriktionen ausgegeben werden..
Das obige Beispiel geht davon aus, dass die Session des Wizard ein o2m Feld namens your_model hat, welches wiederum ein Feld code_restriction_ids hat. Nun wird über alle Einträge iteriert und beim ersten invaliden Ausdruck False sowie die entsprechede Fehledermeldung zurückgegeben.
Form View Erweiterung
Die Anzeige der Felder für die Restriktion könnte wie folgt aussehen:
<notebook> <page string="Global Restrictions" name="restrictions_page"> <field name="code_restriction_ids"> <tree editable="bottom"> <field name="sequence" widget="handle"/> <field name="restriction_code"/> <field name="restriction_message"/> </tree> </field> <separator string="Available Variables"/> <field name="restriction_code_doc"/> </page> </notebook>