Einiges Grundwissen zum Thema Rechenregeln

Virtuemart nutzt für seine eingebundenen Plugins auch die Schnittstelle von Joomla, folglich sind Plugins auch im Joomla-Pluginverzeichnis zu finden.
Für die Kalkulation von MwSt., andere Rechenregeln und die Währungsumrechnungen wurde u.a. das Avalara-SDK/API eingebunden, welches eine PHP-Script-Class/Bibliothek ist.
Die Rechenart für Discount/Rabatte ist Preismodifikator vor Steuern (intern DBTax).

Wann eine Rechenregel überhaupt angewendet werden soll, lässt sich standardmäßig durch vier Kriterien definieren welche sind:

  • Produktkategorien (category)
  • Shoppergruppen (shoppergroup)
  • Land (country)
  • Bundesland/Staat (state)

Diese vier werden nachfolgend nur noch als Kriterien bezeichnet. Für diese ist immer eine Mehrfachauswahl mgl.

Leider gibt es kein Kriterium um Produktpreise herstellerabhängig zu machen. Das ist schade, denn eigentlich nicht gerade unüblich in der kaufmännischen Praxis, denn gibt z.B. ein Hersteller zeitweilig Rabatte, möchte man diese Verkaufsförderung evtl. an Kunden weitergeben. Das Thema kann auch interessant sein, wenn man von herstellerbezogenen Listenpreisen abweichen will.



 

Relevante Scripte für die Anwendung von Rechenregeln in VirtueMart 2

Nachfolgend eine Liste der relevanten Scripte, die auch im Zushg. mit der Erweiterung des Plugins Rechenregeln um ein Kriterium manufacturer ggf. bearbeitet werden müssen:

/administrator/components/com_virtuemart/helpers
/calculationh.php

Dieses Script erzeugt die Klasse welches die Kalkulation ermöglicht.

Hier werden u.a. die verschiedenen Auswahlkriterien definiert, auf die eine Rechenregel angewendet werden soll. Zu diesen gehören: Produktkategorien, Käufergruppen, Land und Region.

Eine der wichtigsten Methoden gatherEffectingRulesForProductPrice() (dt.: Sammeln von sich auswirkenden Regeln für den Produktpreis) wird in diesem Scirpt verwendet (ca. Zeile 244, u.a. für DBTax ca. Zeile 301/472/487) und auch deklariert (ca. Zeile 822).

#_virtuemart_calc_categories
#_virtuemart_calc_countries
#_virtuemart_calc_shoppergroups
#_virtuemart_calc_states
#_virtuemart_calc_manufacturers

Für jedes Einschränkungskriterium gibt es in VM eine Tabelle. Zu den vier bestehenden Tabellen muss dann also eine 5. für die Hersteller (~_manufacturers) hinzugefügt werden.

/administrator/components/com_virtuemart/views/calc
/view.html.php

Dieses VM-Class-Script bereitet die Anzeige des Formulartemplates für die Konfiguration einer Rechenregel vor. Dieses Script muss erweitert werden um in das Konfig.formular die Herstellerauswahl einzubetten.

In $calc wird die Models-Class-Methode getCalc() aus dem Script calc.php bereitgestellt.

/administrator/components/com_virtuemart/views/calc/tmpl/default.php

Dieses Script dient als Template zur Darstellung der Auswahltabelle für die angelegten Rechenregeln. Da es hier Spalten für die Kriterien gibt, muss/kann auch für die Hersteller eine Spalte erzeugt werden.

/administrator/components/com_virtuemart/views/calc/tmpl
/edit_calc.php

Dieses Script erzeugt als Template-Script die Ausgabe des Konfigurationsdialogformulares für eine Rechenregel. Hier muss also das neue Auswahlfeld für den Manufacturer eingefügt werden, wo wie es von view.html.php übergeben wird.

/administrator/components
/com_virtuemart/models
/calc.php

In diesem Script gibt es diverse Methoden die angepasst werden müssen (remove, store, getCalcs, getCalc). Alle 4 Methoden wurden adäquat wie die existierenden Kriterien erweitert. Ebenso im Construtor für addvalidOrderingFieldName

/administrator/components/com_virtuemart/helpers/shopfunctions.php

In diesem Script sind notw. Class-Methoden die z.B. Listen bereitstellen die im o.g. Script view.html.php benötigt werden wie renderVendorList(), renderShopperGroupList(), renderCountryList() und renderStateList() und auch categoryListTree(). Es gibt hier noch keine Methode renderManufacturerList(), diese muss quasi neu integiert werden!

/administrator/components/com_virtuemart/tables/calc_manufacturers.php

Diese Class (extend von VmTableXarray) erzeugt ein Tabellen-Objekt TableCalc_manufacturers. Dieses Script muss komplett neu erstellt werden, so wie ja auch die o.g. Tabelle #_virtuemart_calc_manufacturers neu erstellt wurde.

 

Umsetzung im Detail - Teil 1: Backendverwaltung für Regeln herstellerabhängig

Erzeugen der neuen Tabelle #_virtuemart_calc_manufacturers:

Diese Tabelle #_virtuemart_calc_manufacturers ist quasi eine Adaption der Tabelle #_virtuemart_calc_shoppergroups und wird so erzeugt:

CREATE TABLE IF NOT EXISTS `j25_virtuemart_calc_manufacturers` (
  `id` mediumint(1) unsigned NOT NULL AUTO_INCREMENT,
  `virtuemart_calc_id` smallint(1) unsigned NOT NULL DEFAULT '0',
  `virtuemart_manufacturer_id` smallint(1) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `i_virtuemart_calc_id` (`virtuemart_calc_id`,`virtuemart_manufacturer_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; 

Das Calc-Manufacturers-Tabellen-Objekt erzeugen

Damit diese Tabelle den Scripten auch zur Verfügung gestellt werden kann, gibt es üblich Scripte die für die Tabellen ein Tabellen-Objekt erzeugen. Diese liegen für VM2 immer im Verzeichnis administrator/componentents/com_virtuemart/tables/. Dafür adaptieren wir einfach das Script calc_shoppergroups.php und erstellen so das Script calc_manufacturers.php mit folgendem konkreten Inhalt für die Objektdefinition:

class TableCalc_manufacturers extends VmTableXarray {
	/**
	 * @author St. Kraft
	 * @param $db A database connector object
	 */
	function __construct(&$db){
		parent::__construct('#__virtuemart_calc_manufacturers', 'id', $db);
		$this->setPrimaryKey('virtuemart_calc_id');
		$this->setSecondaryKey('virtuemart_manufacturer_id');
	}
} 

Erweiterung der Methoden in der models/calc.php

In diesem Models-Script gibt es diverse Daten-Methoden für die Kriterien. Zu den 4 vorhanden muss dann in den Methoden remove(), store(), getCalcs() und getCalc() durch Adaptieren die Sequenzen für die manufacturers-Tabelle eingefügt werden:

  1. In der Methode getCalc()
    Hier wird zunächst das xrefTable-Objekt als Instanz über die Construct-Methode load() o.g. Manufacturer-Tableobjektes erzeugt. Dazu wird ca. in Zeile 85 eingefügt:

    /**
     * Mod. St.Kraft 2013-02-24 Herstellerrabatt 
     */
    $xrefTable = $this->getTable('calc_manufacturers');
    $this->_data->virtuemart_manufacturers = $xrefTable->load($this->_id);
    if ( $xrefTable->getError() ) {
    	vmError(get_class( $this ).' calc_manufacturers '.$xrefTable->getError());
    }
     vmdebug('Manufacturers xrefTable',__FILE__,__FUNCTION__, $xrefTable);
    // end Herstellerrabatt
    
  2. In die Methode getCalcs()
    Diese Methode erzeugt lediglich eine Liste mit Elementen die diesem Kriterium zugeordnet sind, ohne jegliche Auswahlfunktion, also z.B. die der Rechenregel zugewiesenen Hersteller. Die Listenelemente haben eine Link zum Eintrag also z.B. zum betreffenden Herstellerpflegeformular und ein Tooltipp. Genutzt wird zur Erzeugung die Methode renderGuiList() aus shopfunctions.php. Hier fügen wir ab ca. Zeile 154 in die calc.php ein:
    $data->calcManufacturersList = shopfunctions::renderGuiList('virtuemart_manufacturer_id','#__virtuemart_calc_manufacturers','virtuemart_calc_id',$data->virtuemart_calc_id,'mf_name','#__virtuemart_manufacturers','virtuemart_manufacturer_id','manufacturer');
    
  3. In die Methode store()
    Diese dient zum Speichern der gemachten Zuweisungen/Multiauswahlen in die Tabelle für das Kriterium. Damit auch für die Hersteller die Speicherung erfolgt, muss hierfür eine Einfügung ergolgen, ab ca. Zeile 226 (es wird die Methode xrefTable->bindChecknStore() verwendet):
    $xrefTable = $this->getTable('calc_manufacturers');
    $xrefTable->bindChecknStore($data);
    if($xrefTable->getError()){
    	vmError('Calculation store '.$xrefTable->getError());
    }
    
  4. In die Methode remove()
    Diese entfernt Einträge für ein Kriterium aus der Datenbanktabelle. Hier sind zwei Einfügungen notw. Zunächst muss beim Eintritt in die Methode die das Tabelleobjekt für calc_manufacturers instanziert werden (ca. Zeile 301). Dann muss weiter unten die Remove-Anweisung für diese Tabelle (Tabellen-Methode delete()) ausgeführt werden (ca. Zeile 338):
    $manufacturers = $this->getTable('calc_manufacturers'); // Mod. St.Kraft 2013-02-24 Herstellerrabatt
    ...
    if (!$manufacturers->delete($id)) {
    	vmError(get_class( $this ).'::remove '.$id.' '.$manufacturers->getError());
    	$ok = false;
    }
    
  5. In den Contructor ___contruct()
    sollte noch das Array $addvalidOrderingFieldName noch um virtuemart_manufacturer_id erweitert werden:
    $this->addvalidOrderingFieldName(array('virtuemart_category_id','virtuemart_country_id','virtuemart_state_id','virtuemart_shoppergroup_id'
    				,'virtuemart_manufacturer_id' // Mod. <mediaDESIGN> St.Kraft 2013-02-24 Herstellerrabatt
    			));
    
  6. ...



 

Die neue Methode ShopFunctions::renderManufacturerList() in shopfunctions.php

Diese Funktion dient dazu eine Multiple-Liste der Hersteller fertig zu liefern, so dass diese innerhalb des Fomulars zur Konfiguration der Rabattregel als neues Kriterium angezeigt werden kann. Prinzipiell funktioniert diese nach dem identischen Prinzip wir bei den Shoppergruppen und sieht konkret so aus (eingefügt ab ca. Zeile: 214):

/**
 * Renders the list of Manufacturers
 *
 * @author St. Kraft
 * Mod. St.Kraft 2013-02-24 Herstellerrabatt 
 */
static public function renderManufacturerList ($manufacturerId = 0, $multiple = FALSE, $name = 'virtuemart_manufacturer_id') {
	$manufacturerModel = VmModel::getModel ('manufacturer');
	$manufacturers = $manufacturerModel->getManufacturers (FALSE, TRUE);
	$attrs = '';
	if ($multiple) {
		$attrs = 'multiple="multiple"';
		if($name=='virtuemart_manufacturer_id')	$name.= '[]';
	} else {
		$emptyOption = JHTML::_ ('select.option', '', JText::_ ('COM_VIRTUEMART_LIST_EMPTY_OPTION'), 'virtuemart_manufacturer_id', 'mf_name');
		array_unshift ($manufacturers, $emptyOption);
	}
	$listHTML = JHTML::_ ('select.genericlist', $manufacturers, $name, $attrs, 'virtuemart_manufacturer_id', 'mf_name', $manufacturerId);
	return $listHTML;
}

 

Nutzung der neuen Methode renderManufacturerList im View-Script view.html.php

Die eben definierte Funktion renderManufactuerList() wollen wir nun in unser Backend-View-Script zur Konfiguration einer Rechenregel als Kriteriumsfeld einfügen. Dazu binden wir folgende zwei Scriptzeilen ab ca. Zeile 113 in die Methode display() im Script administrator/components/com_virtuemart/views/calc/view.html.php ein:

/* Get the Manufacturers 
 * Mod. St.Kraft 2013-02-24 Herstellerrabatt
 */
$manufacturerList= ShopFunctions::renderManufacturerList($calc->virtuemart_manufacturer,true);
$this->assignRef('manufacturerList', $manufacturerList);
// end: Mod. Herstellerrabatt

 

Ausgabe des Multilistfeldes im Formular über das Template-Script edit_calc.php

Das Multilist-Auswahlfeld haben wir nun vorbereitet zur Ausgabe und brauchen es nur noch in unserem Template-Script ausgeben. Dazu wird folgende einzelne Zeile im Script edit_calc.php im Pfad administrator/components/com_virtuemart/views/tmpl/ eingefügt (unterhalb z.B. der Staaten-Liste:

<?php echo VmHTML::row('raw','COM_VIRTUEMART_MANUFACTURER', $this->manufacturerList ); ?>

 

Anpassen der Listenausgabe für die Steuer und Rechenregeln - Listentemplates default.php

Um eine Steuer oder Rechenregel anzupassen gibt es vorher eine Auswahllistenseiten, wo die zu bearbeitende Regel angezeigt und angeklickt werden kann. In dieser Liste werden z.T. auch Kriterien in Spalten angezeigt. Wenn dies auch für die Hersteller erfolgen soll, muss die Listenerzeugung erweitert werden. Dazu wird zum eine o.g. Funktion getCalcs() im Script models/calc.php angepasst werden. Wie das zu erfolgen hat ist oben beschrieben.

Wir haben oben beschrieben, das das Ergebnis aus getCalcs() dem Template über die Variable $data->calcManufacturersList bereitgestellt (u.s. shopfunction::renderGuiList() ).

Für die Ausgabe der Liste ist das Templatescript views/calc/tmpl/default.php verantwortlich. Hier wird $data zeilenweise $row per foreach durchlaufen und per $row-calcManufacturersList die Liste der Hersteller in der Spalte ausgegeben. Im Templatescript muss also zum einen ein Spaltenkopf erzeugt werden (ca. Zeile 60) und zum Anderen der Zellinhalt mit den zugewiesenen Herstellern:

<th><?php echo JText::_('COM_VIRTUEMART_MANUFACTURER'); /* Mod. St.Kraft 2013-02-24 Herstellerrabatt */ ?></th>
<td>
  <?php echo $row->calcManufacturersList; /* Mod. St.Kraft 2013-02-24 Herstellerrabatt */ ?>
</td>



 

Umsetzung im Detail - Teil 2: Berücksichtigung der Herstellerabhängigkeit bei Rabattanwendung auf Preise

Nun wollen wir noch erreichen, dass bei der frontendseitigen Ausgabe der rabattierten Preise die zugewiesenen Herstellerabhängigkeiten berücksichtigt werden. Ein wichtiges Script hierbei ist helpers/calculationh.php (Class: calculationHelper). In den Methode getProductPrices(), calculateCostprice() und calculateCustomPriceWithTax() wird immer wieder die Methode gatherEffectingRulesForProductPrice() (s.o.) verwendet. Diese prüft das Zutreffen von Steuer und Rechenregeln lt. Kriterien auf eine Produktpreismodifikation. Die Methode gatherEffectingRulesForBill() prüft das Zutreffen der Regeln bei Rechnungen. Beide Methoden müssen erweitert werden.

Die Methode gatherEffectingRulesForProductPrice() selbst wird im gleichen Script definiert (ca. Zeile 1113) und um folgende Sequenz erweitert:

if(!isset($this->allrules[$this->productVendorId][$entrypoint][$i]['manufacturers'])){
	$q = 'SELECT `virtuemart_manufacturer_id` FROM #__virtuemart_calc_manufacturers WHERE `virtuemart_calc_id`="' . $rule['virtuemart_calc_id'] . '"';
	$this->_db->setQuery($q);
	$this->allrules[$this->productVendorId][$entrypoint][$i]['manufacturers'] = $this->_db->loadResultArray();
}
$hitsManufacturer = true; if (isset($this->_manufacturerId)) { $hitsManufacturer = $this->testRulePartEffecting($this->allrules[$this->productVendorId][$entrypoint][$i]['manufacturers'], $this->_manufacturerId); }

Die Methode gatherEffectingRulesForBill() im gleichen Script (ca. Zeile 946) wird um folgende fast ähnliche Sequenz erweitert:

$q = 'SELECT `virtuemart_manufacturer_id` FROM #__virtuemart_calc_manufacturers WHERE `virtuemart_calc_id`="' . $rule["virtuemart_calc_id"] . '"';
$this->_db->setQuery($q);
$manufacturers = $this->_db->loadResultArray();
$hitsManufacturer = true;
if (isset($this->_manufacturerId)) {
	$hitsManufacturer = $this->testRulePartEffecting($manufacturers, $this->_manufacturerId);
}

In beiden Erweiterungen ist zu sehen, dass hier die Object-Variable $_manufacturerId verwendet wird. In dieser Variablen wird zum Vergleichen die Hersteller-Id des Herstellers für dieses Produktes übergeben, bzw. erwartet. Diese ist jedoch noch nicht bekannt und muss im Script / Objekt erst belegt werden. Dazu delarieren wir zunächst ganz am Anfang die Varialbe (ca. Zeile 33):

private $_manufacturerId; /* Mod. St.Kraft 2013-02-24 Herstellerrabatt */

In der Methode getProductPrices() wird diese Variable mit Inhalt belegt. Der Vorteil hierbei ist, dass das Product vollständig als Objekt in $product zur Verfügung steht u.a. auch der Hersteller mit der ID (ca. Zeile 267):

if (is_object($product)) {
...
	$this->_manufacturerId = !empty($product->virtuemart_manufacturer_id) ? $product->virtuemart_manufacturer_id:0;
...
}

 

Der Inhalt des Table-Scriptes calc_manufacturers.php

if(!class_exists('VmTableXarray'))require(JPATH_VM_ADMINISTRATOR.DS.'helpers'.DS.'vmtablexarray.php');
class TableCalc_manufacturers extends VmTableXarray {
    /**
     * @author St. Kraft
     * Mod. <mediaDESIGN> St.Kraft 2013-02-24 Herstellerrabatt
     * @param $db A database connector object
     */
    function __construct(&$db){
        parent::__construct('#__virtuemart_calc_manufacturers', 'id', $db);
        $this->setPrimaryKey('virtuemart_calc_id');
        $this->setSecondaryKey('virtuemart_manufacturer_id');
    }
}

 

Das waren alle notw. Änderungen.

Ab Version 2.0.20 sind diese Modifikationen Bestandteil des VirtueMart-Cores geworden.

 

Zur Anwendung und Konfiguration der Herstellerrabatte

Kurz noch ein Hinweis zur Anwendung der herstellerabhängigen Rabattregel und deren Konfiguration.

  • Damit die Regel für Produkte greift, sollte bei Produktendpreis die Regel "Allegemeine Regeln zuordnen" ausgewählt sein. Also nicht der Rabattsatz selbst, das würde ansich die zugewiesenen Kriterien hart überschreiben und dem Produkt in jedem Fall den Rabattsatz zuweisen unabhängig davon ob er ansonsten die Kriterien für den Rabatt erfüllt.

 {jcomments on}

 

Wenn Ihnen dieser Beitrag geholfen und viel Zeit gespart hat, zeigen Sie sich erkenntlich: Über eine Klick auf Google+1 oder Rückmeldungen freue ich mich, zeigt es mir doch, dass sich die Mühe für die Beitragerstellung gelohnt hat. Schenken Sie auch den Produktwerbungen Ihre Beachtung.