Bei vergünstigten Angeboten ist vielfach die Anzeige der Ersparnis als Betrag und in Prozent erwünscht. xt:Commerce bietet “von Haus aus” zumindest die Prozent-Anzeige beim Preisnachlass in Form eines Artikel-Rabatts - Näheres zum Thema “Preisnachlässe” ist diesen beiden Beiträgen zu entnehmen:

Um eine Ersparnis bei jeder Form eines Preisnachlasses ausgeben zu lassen, muss die Datei “xtcPrice.php” (im Ordner “includes”/”classes” bearbeitet werden. So können gleich zwei Aufgaben erledigt werden:

  • Erstens sollte man vermeiden, dass xt:Commerce das Wort “UVP” ausgibt, was in der Standard-Installation dann der Fall ist, wenn man einen Gruppen-Preisnachlass anbietet. Denn wenn man sich nicht 120%ig sicher ist, dass der UVP auch wirklich die aktuelle unverbindliche Preisempfehlung ist, sollte man das Wort am besten aus seinem Wortschatz streichen. Denn nur zu gern werden veraltete UVPs abgemahnt.
  • Zweitens kann man seinen Shop dazu bringen, bei jeder Preis-Anzeige die Ersparnis anzuzeigen. Sei es bei der eigentlichen Artikel-Beschreibung oder bei Artikel-Teasern in den Boxen der linken und rechten Spalte. Und das wäre doch nett, oder?

 

Nicht “UVP” schreiben:

Wer eine solche Ersparnis-Anzeige nicht braucht, sondern nur das Wort “UVP” bei der Preis-Ausgabe vermeiden will, ist ziemlich schnell fertig: Entweder die Sprachdatei ändern - oder in der erwähnten Datei “xtcPrice.php” die Zeile 368 suchen …

$price = '<span class="productOldPrice">'.MSRP.$this->xtcFormat($pPrice, $format).'</span><br />'.YOUR_PRICE.$this->checkAttributes($pID).$this->xtcFormat($sPrice, $format);

… und dort MSRP durch INSTEAD austauschen. Damit wird anstelle von “UVP” das weitaus unverfänglichere Wörtchen “statt” ausgegeben.

Ob man allerdings wegen einer Änderung, die im Grunde genommen nur das “Vokabular” betrifft, gleich an einer Systemdatei herumbasteln möchte, ist Geschmacksache. Für Händler mit mehrsprachigen Shops, die vielleicht in Zukunft noch das ein oder andere Sprachpaket zusätzlich installieren möchten, ist die oben beschriebene Änderung jedoch vermutlich die einfachere Lösung.

 

Was berechnet werden kann:

Zunächst einmal muss man sich darüber Gedanken machen, welche Preisnachlässe in “xtcPrice.php” berücksichtigt werden. Das sind folgende Varianten:

  • Gruppenpreise (Beispiel: “Für Stammkunden billiger als für Neukunden”)
  • Artikel-Rabatt (Beispiel: “Dieser Artikel wird je nach Kundengruppe maximal so-und-so-viel Prozent billiger verkauft”)
  • Sonderpreis (ersetzt alle anderen möglichen Preise und Preisnachlässe, begrenzbar auf einen bestimmten Zeitraum und ein bestimmtes Kontingent)

Der Preisnachlass in Form von “Rabatt auf gesamte Bestellung” wird in “xtcPrice.php” nicht beachtet, dort “weiß” das System quasi noch nichts davon - und auch im Shop sieht man das Ergebnis erst am Gesamtwert des Warenkorbs. Ebenso sind die einzelnen Werte der “Staffelpreise” hier nicht relevant, denn in der “xtcPrice.php” wird die Ausgabe des Preises bei Kauf eines einzelnen Artikels bestimmt.

 

Die “betroffenen” Funktionen

An der Art und Weise wie Preisnachlässe behandelt werden sollte nichts Wesentliches verändert werden - daher konzentriert sich die Suche nach der Ausgabe von Preisen auf die “Format-Funktionen”, die in der Datei “xtcPrice.php” (durch ein paar deutliche Kommentarzeilen 298 bis 304 gekennzeichnet) erst ab Zeile 306 beginnen.

Ein allgemeiner Hinweis vorweg: Geänderte Zeilen sind bei den folgenden Code-Beispielen mit Kommentar-Marken gekennzeichnet. Der Original-Code ist zudem nie gelöscht, sondern nur auskommentiert.


1) Preisformatierung - Nicht verändern!

Die Funktion “xtcFormat” übernimmt dabei ein paar “grundsätzliche” Aufgaben wie beispielsweise das Runden auf zwei Stellen nach dem Komma plus Ausgabe des eingestellten Währungs-Symbols. Die kann so bleiben, wie sie ist.


2) “Artikel-Rabatt” - Erweitern

An der nächsten Funktion “xtcFormatSpecialDiscount” (Zeile 334 bis 346) gibt’s etwas zu tun. Hier wird im Falle eines Artikel-Rabatts dieser im Vergleich mit dem normalen Artikelpreis ausgegeben.


function xtcFormatSpecialDiscount($pID, $discount, $pPrice, $format, $vpeStatus = 0) {
	$sPrice = $pPrice - ($pPrice / 100) * $discount;
	if ($format) {
		$price = '<span class="productOldPrice">'.INSTEAD.$this->xtcFormat($pPrice, $format).'</span><br />'.ONLY.$this->checkAttributes($pID).$this->xtcFormat($sPrice, $format).'<br />'.YOU_SAVE.$discount.'%';
		if ($vpeStatus == 0) {
			return $price;
		} else {
			return array ('formated' => $price, 'plain' => $sPrice);
		}
	} else {
		return round($sPrice, $this->currencies[$this->actualCurr]['decimal_places']);
	}
}
	

Dort kann man schon eine grundlegende Verfahrensweise erahnen - der Artikel-”Grund”-Preis steckt in der Variablen $pPrice, der reduzierte Preis in $sPrice

  • Die Berechnung der Ersparnis als Betrag ist kein Problem, hier muss lediglich eine Differenz gebildet und entsprechend der eingestellten Währung formatiert werden - und zwar mit der erwähnten Funktion “xtcFormat” - Der Code für diese Berechnung lautet also:

    $this->xtcFormat($pPrice-$sPrice, $format)

  • Die Ersparnis in Prozent erfordert ein bisschen Dreisatz - Der Rechenweg ist: (Artikel-Preis minus Reduzierter-Preis) geteilt durch Artikel-Preis mal 100 - In PHP geschrieben also:

    ($pPrice-$sPrice) / $pPrice * 100

  • Um endlos viele Nachkommastellen auszuschließen, runden wir das Ganze und versehen die Ausgabe mit dem “Sie sparen”-Text sowie einem Prozent-Zeichen:

    YOU_SAVE.round(($pPrice-$sPrice) / $pPrice * 100).' %'

Und was ist mit $discount? In diesem Falle entspricht der Discount dem Preisnachlass - Und um eine (passend zu den folgenden Preisnachlässen) einheitliche Anzeige zu gewährleisten, lassen wir diesen Discount (Artikel-Rabatt) auch nicht unerwähnt.

Damit ändert sich der gesamte Bereich wie folgt:


function xtcFormatSpecialDiscount($pID, $discount, $pPrice, $format, $vpeStatus = 0) {
	$sPrice = $pPrice - ($pPrice / 100) * $discount;
	if ($format) {
		//$price = '<span class="productOldPrice">'.INSTEAD.$this->xtcFormat($pPrice, $format).'</span><br />'.ONLY.$this->checkAttributes($pID).$this->xtcFormat($sPrice, $format).'<br />'.YOU_SAVE.$discount.'%';
		$price = '<span class="productOldPrice"><small>'.INSTEAD.'</small><del>'.$this->xtcFormat($pPrice, $format).'</del></span><br />'.ONLY.$this->checkAttributes($pID).$this->xtcFormat($sPrice, $format).'<br /><small>'.YOU_SAVE.round(($pPrice-$sPrice) / $pPrice * 100).' % /'.$this->xtcFormat($pPrice-$sPrice, $format);
		// Ausgabe des gültigen Kundengruppen-Rabatts (sofern vorhanden)
		if ($discount != 0)
				{ $price .= '<br />'.BOX_LOGINBOX_DISCOUNT.': '.round($discount).' %'; }
			$price .= '</small>';
		if ($vpeStatus == 0) {
			return $price;
		} else {
			return array ('formated' => $price, 'plain' => $sPrice);
		}
	} else {
		return round($sPrice, $this->currencies[$this->actualCurr]['decimal_places']);
	}
}
	

BOX_LOGINBOX_DISCOUNT greift dabei auf eine global verfügbare Text-Variable zurück - In der Deutschen Sprachdatei wird bei deren Aufruf das Wort “Artikelrabatt” zurückgegeben. Passt an dieser Stelle optimal.

Die Details an der HTML-Ausgabe sind natürlich Geschmackssache - Ich halte es jedenfalls für sinnvoll, den nicht mehr gültigen, alten Preis unabhängig von CSS Style-Anweisungen schon gleich im HTML-Code mittels eines <DEL>-Tags durchzustreichen.


3) Sonderpreis - Erweitern

Liegt ein “Sonderpreis” vor, werden alle anderen angelegten Preisnachlässe ungültig. Entsprechend einfach ist die Original-Funktion “xtcFormatSpecial” - zu finden in den Zeilen 348 bis 359:


function xtcFormatSpecial($pID, $sPrice, $pPrice, $format, $vpeStatus = 0) {
	if ($format) {
		$price = '<span class="productOldPrice">'.INSTEAD.$this->xtcFormat($pPrice, $format).'</span><br />'.ONLY.$this->checkAttributes($pID).$this->xtcFormat($sPrice, $format);
		if ($vpeStatus == 0) {
			return $price;
		} else {
			return array ('formated' => $price, 'plain' => $sPrice);
		}
	} else {
		return round($sPrice, $this->currencies[$this->actualCurr]['decimal_places']);
	}
}
	

Nachlässe via “Discount” brauchen nicht beachtet zu werden, denn sie werden von Sonderpreisen sowieso “überschrieben” - daher ergibt sich (in Anlehnung an die oben beschriebene Erweiterung) folgender, neuer Code:


function xtcFormatSpecial($pID, $sPrice, $pPrice, $format, $vpeStatus = 0) {
	if ($format) {
		//$price = '<span class="productOldPrice">'.INSTEAD.$this->xtcFormat($pPrice, $format).'</span><br />'.ONLY.$this->checkAttributes($pID).$this->xtcFormat($sPrice, $format);
		$price = '<span class="productOldPrice"><small>'.INSTEAD.'</small><del>'.$this->xtcFormat($pPrice, $format).'</del></span><br />'.ONLY.$this->checkAttributes($pID).$this->xtcFormat($sPrice, $format).'<br /><small>'.YOU_SAVE.round(($pPrice-$sPrice) / $pPrice * 100).' % /'.$this->xtcFormat($pPrice-$sPrice, $format).'</small>';
		if ($vpeStatus == 0) {
			return $price;
		} else {
			return array ('formated' => $price, 'plain' => $sPrice);
		}
	} else {
		return round($sPrice, $this->currencies[$this->actualCurr]['decimal_places']);
	}
}
	


4) Preisnachlass bei Staffelpreisen

Die Funktion “xtcFormatSpecialGraduated” (Zeile 361 bis 380) kombiniert alle vorliegenden Preisnachlässe mit Mengen-Staffelungen in folgender Weise:

  1. Die Existenz von Preis-Staffelungen wird überprüft und - abhängig davon, ob diese gültig sind (und nicht etwa von einem “Sonderangebot” überschrieben wurden) - ein “Ab” vor den Kaufpreis für einen einzelnen Artikel zu gesetzt
  2. Zudem wird das nötige Verhalten im Falle anderer Preisnachlässe nach Vorbild der oben genannten Funktionen ausgelöst
  3. Der sich ergebende Einzel-Preis wird mittels der Funktion “xtcFormat” (siehe oben) in der eingestellten Währung mit oder ohne eingerechnete Steuer ausgegeben

function xtcFormatSpecialGraduated($pID, $sPrice, $pPrice, $format, $vpeStatus = 0, $pID) {
	if ($pPrice == 0)
		return $this->xtcFormat($sPrice, $format, 0, false, $vpeStatus);
	if ($discount = $this->xtcCheckDiscount($pID))
		$sPrice -= $sPrice / 100 * $discount;
	if ($format) {
		if ($sPrice != $pPrice) {
			$price = '<span class="productOldPrice">'.MSRP.$this->xtcFormat($pPrice, $format).'</span><br />'.YOUR_PRICE.$this->checkAttributes($pID).$this->xtcFormat($sPrice, $format);
		} else {
			$price = FROM.$this->xtcFormat($sPrice, $format);
		}
		if ($vpeStatus == 0) {
			return $price;
		} else {
			return array ('formated' => $price, 'plain' => $sPrice);
		}
	} else {
		return round($sPrice, $this->currencies[$this->actualCurr]['decimal_places']);
	}
}
	

Einzelpreise “interessieren” sich jedoch nicht für Preis-Staffelungen. Gibt es einen Rabatt, wird er abgezogen - gibt es einen Sonderpreis, ist nur dieser gültig. Gibt es keins von beiden, gilt der Gruppenpreis für einen einzelnen Artikel. Gibt es nur Preisnachlass in Form von Mengenstaffelungen, bleibt der Einzelpreis wiederum unbeeinflusst …

Also unterscheidet sich die Ersparnis-Berechnung wieder einmal kaum von den oben erklärten Beispielen:


function xtcFormatSpecialGraduated($pID, $sPrice, $pPrice, $format, $vpeStatus = 0, $pID) {
	if ($pPrice == 0)
		return $this->xtcFormat($sPrice, $format, 0, false, $vpeStatus);
	if ($discount = $this->xtcCheckDiscount($pID))
		$sPrice -= $sPrice / 100 * $discount;
	if ($format) {
		if ($sPrice != $pPrice) {
			//$price = '<span class="productOldPrice">'.MSRP.$this->xtcFormat($pPrice, $format).'</span><br />'.YOUR_PRICE.$this->checkAttributes($pID).$this->xtcFormat($sPrice, $format);
			$price = '<span class="productOldPrice"><small>'.INSTEAD.'</small><del>'.$this->xtcFormat($pPrice, $format).'</del></span><br />'.ONLY.$this->checkAttributes($pID).$this->xtcFormat($sPrice, $format).'<br /><small>'.YOU_SAVE.round(($pPrice-$sPrice) / $pPrice * 100).' % /'.$this->xtcFormat($pPrice-$sPrice, $format);
			// Ausgabe des gültigen Kundengruppen-Rabatts (sofern vorhanden)
			if ($discount != 0)
				{ $price .= '<br />'.BOX_LOGINBOX_DISCOUNT.': '.round($discount).' %'; }
			$price .= '</small>';
		} else {
			$price = FROM.$this->xtcFormat($sPrice, $format);
		}
		if ($vpeStatus == 0) {
			return $price;
		} else {
			return array ('formated' => $price, 'plain' => $sPrice);
		}
	} else {
		return round($sPrice, $this->currencies[$this->actualCurr]['decimal_places']);
	}
}
	

 

Download und Installation

Die Datei “xtcPrice.php” gibt’s hier mit allen beschriebenen Veränderungen zum Download.

Anleitung: Runterladen, entpacken - und (natürlich nach vorheriger Sicherheitskopie) mit der entsprechenden Datei im Ordner “includes”/”classes” Ihres Shop-Systems austauschen.

Ich bitte zu beachten, dass dieser Austausch JEDEN angezeigten Preis in Ihrem Shop beeinflusst. Eventuell könnte die von mir vorgeschlagene HTML-Ausgabe für ungewünschte Ergebnisse in Ihrem Layout sorgen - Sollte dies der Fall sein, überarbeiten Sie bitte die entsprechend markierten Stellen im Quellcode nach Ihren Anforderungen.

 

Zusammenfassung:

Nach dem Einbau aller beschriebenen Änderungen bzw. nach dem Upload der zur Verfügung gestellten Datei verhalten sich alle Preis-Angaben des xt:Commerce-Shops überall (!) wie folgt:

  1. Unveränderte (Normal-)Preise werden wie gewohnt angezeigt
  2. Das Wort “UVP” taucht im Shop nicht mehr auf
  3. Liegt ein - gemessen am Normalpreis des Artikels - in irgendeiner Form reduziertes Angebot vor (Sonderangebot, Artikelrabatt oder vergünstigter Gruppenpreis), wird zuerst der allgemeine Einzel-Preis in durchgestrichener Form ausgegeben. Darunter der gültige reduzierte Einzelpreis, nach Zeilenumbruch die Ersparnis in Prozent, Schrägstrich, Ersparnis als Betrag in eingestellter Währung.
  4. Sofern (zusätzlich oder ausschließlich) ein Artikel-Rabatt gilt, erscheint dieser in einer vierten Zeile als weiterer Prozentwert.
  5. Unabhängig ob Netto- oder Bruttopreise ausgegeben werden und unabhängig von der gewählten Kombination aus den bei xt:Commerce möglichen Preisnachlässen - Alle Berechnungen zeigen den Preis an, mit dem ein einzelner Artikel im Warenkorb landet und vergleichen diesen mit dem Normalpreis

Nicht mehr mit Reizen geizen: Jegliche Art eines Preisnachlasses sollte nun bei jedem Preis deutlich in “gesparte Prozent” und “gespartem Betrag” angezeigt werden.

 

 

Die angebotene Datei ist für xt:Commerce 3.04 SP1 erstellt und auch nur mit dieser Version getestet. Sollten Sie eine andere Version des Shops installiert haben, rate ich von der Verwendung ab. Einer Haftung für eventuelle System-Ausfälle widerspreche ich außerdem ausdrücklich.

Bitte beachten Sie unbedingt die allgemeinen Hinweise zur Verwendung hier veröffentlichter Code-Beispiele!

 

Nachtrag: Ähnliche Funktionen mit Template-Files

Wenn man eine Änderung von System-Dateien vermeiden möchte, kann man ähnliche Funktionen auch ausschließlich mit Template-Dateien realisieren. Eine solche Funktions-Erweiterung des Templates wird in diesem Beitrag vorgestellt.