Table of Content

    1.0.1.0.0

    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>