Import aus Monorepo (frischer Start)
This commit is contained in:
422
skrift-preisrechner.php
Normal file
422
skrift-preisrechner.php
Normal file
@@ -0,0 +1,422 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: Skrift Preisrechner
|
||||
* Description: Preisrechner für Briefe/Postkarten per Shortcode. Holt Preisdaten stündlich per WP-Cron aus dem Skrift-Backend (öffentliche Read-API, ohne Token) und speichert sie in WordPress. Die Anzeige rechnet ohne Netzwerkzugriff – dadurch ohne Verzögerung. Shortcodes: [skrift_preis_inkl] und [skrift_preis_exkl].
|
||||
* Version: 2.0.0
|
||||
* Author: Skrift
|
||||
*
|
||||
* Sicherheit: Es werden ausschließlich ÖFFENTLICHE Werte gelesen (Basispreis,
|
||||
* Formel-Parameter, MwSt, Porto) – keine Secrets, kein API-Token.
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
require_once __DIR__ . '/formel.php';
|
||||
|
||||
const SKRIFT_PREIS_OPTION = 'skrift_preis_daten';
|
||||
const SKRIFT_PREIS_CRON = 'skrift_preis_sync';
|
||||
/** Nach dieser Zeit gelten gespeicherte Daten als veraltet (Cron-Ausfall). */
|
||||
const SKRIFT_PREIS_MAX_ALTER = 7200; // 2 Stunden
|
||||
|
||||
/** Basis-URL des Skrift-Backends (Directus). Per Filter überschreibbar. */
|
||||
function skrift_preis_backend_url() {
|
||||
return rtrim(apply_filters('skrift_preis_backend_url', 'https://admin.skrift.de'), '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Holt die Preisdaten frisch aus Directus und legt sie in einer WP-Option ab.
|
||||
* Wird stündlich per Cron aufgerufen – nicht im normalen Seitenaufruf.
|
||||
* Bei Fehlern bleiben die zuletzt gespeicherten Daten erhalten.
|
||||
*/
|
||||
function skrift_preis_sync() {
|
||||
$base = skrift_preis_backend_url();
|
||||
$fehler = null;
|
||||
$hole = function ($pfad) use ($base, &$fehler) {
|
||||
$res = wp_remote_get($base . $pfad, array('timeout' => 10));
|
||||
if (is_wp_error($res)) {
|
||||
if ($fehler === null) $fehler = 'Backend nicht erreichbar (' . $res->get_error_message() . ')';
|
||||
return null;
|
||||
}
|
||||
$code = wp_remote_retrieve_response_code($res);
|
||||
if ($code !== 200) {
|
||||
if ($fehler === null) $fehler = 'Backend antwortete mit HTTP ' . $code . ' auf ' . $pfad;
|
||||
return null;
|
||||
}
|
||||
$json = json_decode(wp_remote_retrieve_body($res), true);
|
||||
if (!is_array($json) || !array_key_exists('data', $json)) {
|
||||
if ($fehler === null) $fehler = 'Unerwartete Antwort auf ' . $pfad;
|
||||
return null;
|
||||
}
|
||||
return $json['data'];
|
||||
};
|
||||
|
||||
$produkte = $hole('/items/products?fields=key,name,base_price,norm_qty,price_factor_a,price_factor_e,pricing_mode,type&filter[active][_eq]=true&limit=-1');
|
||||
$settings = $hole('/items/pricing_settings?fields=*');
|
||||
$items = $hole('/items/price_items?fields=key,price,unit,category&filter[active][_eq]=true&limit=-1');
|
||||
|
||||
// Nur bei Erfolg überschreiben – sonst bleiben die alten Werte stehen.
|
||||
if ($fehler === null && is_array($produkte) && !empty($produkte)) {
|
||||
update_option(SKRIFT_PREIS_OPTION, array(
|
||||
'produkte' => $produkte,
|
||||
'settings' => is_array($settings) ? $settings : array(),
|
||||
'items' => is_array($items) ? $items : array(),
|
||||
'zeit' => time(),
|
||||
'fehler' => null,
|
||||
), false); // kein autoload – wird nur bei Bedarf geladen
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fehler am vorhandenen Datensatz vermerken (für die Diagnose in der Ausgabe).
|
||||
$alt = get_option(SKRIFT_PREIS_OPTION);
|
||||
if (is_array($alt)) {
|
||||
$alt['fehler'] = $fehler;
|
||||
update_option(SKRIFT_PREIS_OPTION, $alt, false);
|
||||
} else {
|
||||
update_option(SKRIFT_PREIS_OPTION, array(
|
||||
'produkte' => array(), 'settings' => array(), 'items' => array(),
|
||||
'zeit' => 0, 'fehler' => $fehler,
|
||||
), false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
add_action(SKRIFT_PREIS_CRON, 'skrift_preis_sync');
|
||||
|
||||
/** Stündlichen Cron beim Aktivieren einplanen und sofort einmal synchronisieren. */
|
||||
register_activation_hook(__FILE__, function () {
|
||||
if (!wp_next_scheduled(SKRIFT_PREIS_CRON)) {
|
||||
wp_schedule_event(time() + 60, 'hourly', SKRIFT_PREIS_CRON);
|
||||
}
|
||||
skrift_preis_sync();
|
||||
});
|
||||
register_deactivation_hook(__FILE__, function () {
|
||||
$ts = wp_next_scheduled(SKRIFT_PREIS_CRON);
|
||||
if ($ts) wp_unschedule_event($ts, SKRIFT_PREIS_CRON);
|
||||
});
|
||||
|
||||
/** Sicherheitsnetz: fehlt der Cron-Eintrag (z. B. nach Migration), neu planen. */
|
||||
add_action('init', function () {
|
||||
if (!wp_next_scheduled(SKRIFT_PREIS_CRON)) {
|
||||
wp_schedule_event(time() + 60, 'hourly', SKRIFT_PREIS_CRON);
|
||||
}
|
||||
// Manuelles Aktualisieren als Admin: ?skrift_preis_refresh=1
|
||||
if (isset($_GET['skrift_preis_refresh']) && current_user_can('manage_options')) {
|
||||
skrift_preis_sync();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Liefert die gespeicherten Preisdaten. Kein HTTP im Normalfall.
|
||||
* Nur wenn noch nie synchronisiert wurde (oder die Daten deutlich veraltet
|
||||
* sind), wird einmalig nachgeholt.
|
||||
*/
|
||||
function skrift_preis_daten() {
|
||||
$d = get_option(SKRIFT_PREIS_OPTION);
|
||||
$leer = !is_array($d) || empty($d['produkte']);
|
||||
$alt = is_array($d) && isset($d['zeit']) && (time() - (int) $d['zeit']) > SKRIFT_PREIS_MAX_ALTER;
|
||||
if ($leer || $alt) {
|
||||
skrift_preis_sync();
|
||||
$d = get_option(SKRIFT_PREIS_OPTION);
|
||||
}
|
||||
if (!is_array($d)) {
|
||||
$d = array('produkte' => array(), 'settings' => array(), 'items' => array(), 'zeit' => 0, 'fehler' => 'Keine Daten.');
|
||||
}
|
||||
return $d;
|
||||
}
|
||||
|
||||
/** Einen Preis-Positionswert (z. B. porto_inland) aus dem Katalog lesen. */
|
||||
function skrift_preis_item($items, $key) {
|
||||
foreach ($items as $it) {
|
||||
if (isset($it['key']) && $it['key'] === $key) return (float) $it['price'];
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/** Ein Produkt per Schlüssel finden. */
|
||||
function skrift_preis_produkt($produkte, $key) {
|
||||
foreach ($produkte as $p) {
|
||||
if (isset($p['key']) && $p['key'] === $key) return $p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die für die Rechnung nötigen Parameter eines Produkts – Grundlage sowohl für
|
||||
* die PHP-Rechnung als auch für die identische Rechnung im Browser.
|
||||
*/
|
||||
function skrift_preis_parameter($daten, $produktKey, $typ = 'privat') {
|
||||
$s = $daten['settings'];
|
||||
$p = skrift_preis_produkt($daten['produkte'], $produktKey);
|
||||
if (!$p) return null;
|
||||
|
||||
// Normalpreis-Menge kommt vom Produkt; ohne Wert kein Kleinmengen-Aufschlag.
|
||||
$norm = isset($p['norm_qty']) ? (float) $p['norm_qty'] : 0.0;
|
||||
// Kleinmengen-Aufschlag je Produkt (a = Deckel, e = Exponent). Leer → 1 / 3.
|
||||
$a = (isset($p['price_factor_a']) && $p['price_factor_a'] !== null && $p['price_factor_a'] !== '') ? (float) $p['price_factor_a'] : 1.0;
|
||||
$e = (isset($p['price_factor_e']) && $p['price_factor_e'] !== null && $p['price_factor_e'] !== '') ? (float) $p['price_factor_e'] : 3.0;
|
||||
// Eine gemeinsame Formel.
|
||||
$formel = !empty($s['multiplier_formula']) ? $s['multiplier_formula'] : '';
|
||||
|
||||
$tiers = array();
|
||||
if (isset($s['quantity_discounts']) && is_array($s['quantity_discounts'])) {
|
||||
foreach ($s['quantity_discounts'] as $t) {
|
||||
$tiers[] = array(
|
||||
'min_qty' => isset($t['min_qty']) ? (int) $t['min_qty'] : 0,
|
||||
'percent' => isset($t['percent']) ? (float) $t['percent'] : 0.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'base' => isset($p['base_price']) ? (float) $p['base_price'] : 0.0,
|
||||
'norm' => $norm,
|
||||
'a' => $a,
|
||||
'e' => $e,
|
||||
'formel' => $formel,
|
||||
'mwst' => isset($s['mwst_percent']) ? (float) $s['mwst_percent'] : 19.0,
|
||||
'tiers' => $tiers,
|
||||
'porto' => skrift_preis_item($daten['items'], 'porto_inland'),
|
||||
'name' => isset($p['name']) ? $p['name'] : $produktKey,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kernrechnung in PHP (spiegelt die Standard-Formel des Backends):
|
||||
* grund = base * menge * multiplikator
|
||||
* multiplik. = menge >= norm ? 1 : 2 - sqrt(menge / norm)
|
||||
* danach Mengenrabatt-Staffel, dann / menge = Preis pro Stück (netto).
|
||||
*/
|
||||
function skrift_preis_rechne_param($par, $menge, $mitKuvert) {
|
||||
if (!$par) return null;
|
||||
$menge = max(1, (int) $menge);
|
||||
|
||||
// Multiplikator exakt mit der in Directus hinterlegten Formel berechnen.
|
||||
// Ohne Normalpreis-Menge am Produkt greift kein Kleinmengen-Aufschlag.
|
||||
$mult = 1.0;
|
||||
if ($par['norm'] > 0) {
|
||||
$mult = skrift_formel_eval($par['formel'], array(
|
||||
'qty' => $menge, 'menge' => $menge,
|
||||
'norm' => $par['norm'], 'base_price' => $par['base'],
|
||||
'a' => $par['a'], 'e' => $par['e'],
|
||||
), 1.0);
|
||||
}
|
||||
if (!is_finite($mult) || $mult <= 0) $mult = 1.0;
|
||||
|
||||
$grund = $par['base'] * $menge * $mult;
|
||||
|
||||
$besteMin = -1; $bestePercent = 0.0;
|
||||
foreach ($par['tiers'] as $t) {
|
||||
if ($menge >= $t['min_qty'] && $t['percent'] > 0 && $t['min_qty'] > $besteMin) {
|
||||
$besteMin = $t['min_qty']; $bestePercent = $t['percent'];
|
||||
}
|
||||
}
|
||||
if ($bestePercent > 0) $grund *= (1 - $bestePercent / 100);
|
||||
|
||||
$netto = $grund / $menge;
|
||||
if ($mitKuvert) $netto += $par['porto'];
|
||||
|
||||
return array(
|
||||
'netto' => round($netto, 2),
|
||||
'brutto' => round($netto * (1 + $par['mwst'] / 100), 2),
|
||||
'mwst' => $par['mwst'],
|
||||
);
|
||||
}
|
||||
|
||||
/** REST-Endpunkt bleibt bestehen (PHP-Rechnung), z. B. für eigene Integrationen. */
|
||||
add_action('rest_api_init', function () {
|
||||
register_rest_route('skrift/v1', '/preis', array(
|
||||
'methods' => 'GET',
|
||||
'permission_callback' => '__return_true',
|
||||
'callback' => function (WP_REST_Request $req) {
|
||||
$daten = skrift_preis_daten();
|
||||
$par = skrift_preis_parameter(
|
||||
$daten,
|
||||
sanitize_text_field($req->get_param('produkt')),
|
||||
$req->get_param('typ') === 'unternehmen' ? 'unternehmen' : 'privat'
|
||||
);
|
||||
$r = skrift_preis_rechne_param(
|
||||
$par,
|
||||
(int) $req->get_param('menge'),
|
||||
$req->get_param('kuvert') === '1' || $req->get_param('kuvert') === 'true'
|
||||
);
|
||||
if (!$r) {
|
||||
$grund = !empty($daten['fehler']) ? $daten['fehler'] : 'Produkt nicht gefunden.';
|
||||
return new WP_Error('skrift_preis', $grund, array('status' => 404));
|
||||
}
|
||||
return rest_ensure_response($r);
|
||||
},
|
||||
));
|
||||
});
|
||||
|
||||
/**
|
||||
* Darstellung. $mitMwst bestimmt, WELCHER Preis gezeigt wird – der Besucher
|
||||
* wählt das bewusst nicht selbst.
|
||||
*
|
||||
* Die Parameter stehen im Seitenquelltext; die Neuberechnung passiert dadurch
|
||||
* sofort im Browser (kein Netzwerkzugriff, keine Latenz) mit exakt derselben
|
||||
* Formel wie in PHP.
|
||||
*/
|
||||
function skrift_preis_render($atts, $mitMwst) {
|
||||
$a = shortcode_atts(array('produkt' => 'briefe', 'typ' => 'privat', 'menge' => 100), $atts);
|
||||
$daten = skrift_preis_daten();
|
||||
$par = skrift_preis_parameter($daten, $a['produkt'], $a['typ']);
|
||||
|
||||
if (!$par) {
|
||||
if (!empty($daten['fehler'])) {
|
||||
return '<p><strong>Preisrechner nicht verfügbar.</strong><br><span style="color:#64748b;font-size:13px;">'
|
||||
. esc_html($daten['fehler']) . '</span></p>';
|
||||
}
|
||||
$bekannt = array();
|
||||
foreach ($daten['produkte'] as $pp) { if (isset($pp['key'])) $bekannt[] = $pp['key']; }
|
||||
return '<p><strong>Preisrechner:</strong> Produkt „' . esc_html($a['produkt']) . '" nicht gefunden.'
|
||||
. (count($bekannt) ? '<br><span style="color:#64748b;font-size:13px;">Verfügbar: '
|
||||
. esc_html(implode(', ', $bekannt)) . '</span>' : '') . '</p>';
|
||||
}
|
||||
|
||||
$startMenge = max(1, (int) $a['menge']);
|
||||
$start = skrift_preis_rechne_param($par, $startMenge, false);
|
||||
$startWert = $mitMwst ? $start['brutto'] : $start['netto'];
|
||||
$hinweis = $mitMwst
|
||||
? 'inkl. ' . rtrim(rtrim(number_format($par['mwst'], 1, ',', '.'), '0'), ',') . ' % MwSt.'
|
||||
: 'zzgl. MwSt.';
|
||||
|
||||
$id = 'skpreis_' . wp_rand(1000, 9999);
|
||||
|
||||
ob_start(); ?>
|
||||
<div class="sk-preisrechner" id="<?php echo esc_attr($id); ?>" style="width:100%;box-sizing:border-box;color:#1A1A1A;font-family:var(--e-global-typography-text-font-family, inherit);font-weight:var(--e-global-typography-text-font-weight, inherit);">
|
||||
<label style="display:block;font-size:0.875em;margin-bottom:8px;color:#1A1A1A;">Anzahl</label>
|
||||
<input type="number" min="1" value="<?php echo esc_attr($startMenge); ?>" class="skp-menge"
|
||||
style="width:100%;box-sizing:border-box;padding:12px 14px;border:1px solid #d8dde5;border-radius:8px;font-family:inherit;font-size:1em;color:#1A1A1A;margin-bottom:18px;">
|
||||
<label style="display:flex;align-items:flex-start;gap:10px;font-size:0.95em;color:#1A1A1A;margin-bottom:20px;cursor:pointer;">
|
||||
<input type="checkbox" class="skp-kuvert" style="margin-top:4px;flex:0 0 auto;">
|
||||
<span>Mit handbeschriebenem Kuvert und Direktversand an die Empfänger</span>
|
||||
</label>
|
||||
<div style="border-top:1px solid #e6e6e6;padding-top:16px;display:flex;justify-content:space-between;align-items:baseline;gap:16px;">
|
||||
<span style="font-size:1em;">Preis pro Stück</span>
|
||||
<strong class="skp-wert" style="font-size:1.5em;font-family:var(--e-global-typography-primary-font-family, inherit);white-space:nowrap;"><?php echo esc_html(number_format($startWert, 2, ',', '.')); ?> €</strong>
|
||||
</div>
|
||||
<div style="text-align:right;font-size:0.8em;opacity:.65;margin-top:4px;"><?php echo esc_html($hinweis); ?></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
var wrap = document.getElementById(<?php echo wp_json_encode($id); ?>);
|
||||
if (!wrap) return;
|
||||
var par = <?php echo wp_json_encode($par); ?>;
|
||||
var mitMwst = <?php echo $mitMwst ? 'true' : 'false'; ?>;
|
||||
var menge = wrap.querySelector('.skp-menge');
|
||||
var kuvert = wrap.querySelector('.skp-kuvert');
|
||||
var wert = wrap.querySelector('.skp-wert');
|
||||
|
||||
// Formel-Interpreter (Spiegel der PHP-Fassung) – wertet die in Directus
|
||||
// hinterlegte Formel aus, damit Änderungen automatisch mitwirken.
|
||||
function formelEval(ausdruck, vars) {
|
||||
if (!ausdruck) return 1;
|
||||
var s = String(ausdruck).replace(/Math\./g, '').replace(/%(\w+)%/g, '$1');
|
||||
var T = [], i = 0;
|
||||
while (i < s.length) {
|
||||
var c = s[i];
|
||||
if (/\s/.test(c)) { i++; continue; }
|
||||
if (/[0-9]/.test(c) || (c === '.' && /[0-9]/.test(s[i + 1] || ''))) {
|
||||
var st = i; while (i < s.length && /[0-9.]/.test(s[i])) i++;
|
||||
T.push(['num', parseFloat(s.slice(st, i))]); continue;
|
||||
}
|
||||
if (/[A-Za-z_]/.test(c)) {
|
||||
var sa = i; while (i < s.length && /[A-Za-z0-9_]/.test(s[i])) i++;
|
||||
T.push(['id', s.slice(sa, i)]); continue;
|
||||
}
|
||||
var zwei = s.substr(i, 2);
|
||||
if (['>=', '<=', '==', '!='].indexOf(zwei) >= 0) { T.push(['op', zwei]); i += 2; continue; }
|
||||
T.push(['op', c]); i++;
|
||||
}
|
||||
var k = 0;
|
||||
function ist(w) { return k < T.length && T[k][0] === 'op' && T[k][1] === w; }
|
||||
function fn(n, a) {
|
||||
var x = a[0] || 0;
|
||||
switch (n) {
|
||||
case 'sqrt': return x < 0 ? 0 : Math.sqrt(x);
|
||||
case 'pow': return Math.pow(x, a[1] || 0);
|
||||
case 'abs': return Math.abs(x);
|
||||
case 'min': return Math.min.apply(null, a);
|
||||
case 'max': return Math.max.apply(null, a);
|
||||
case 'round': return Math.round(x);
|
||||
case 'floor': return Math.floor(x);
|
||||
case 'ceil': return Math.ceil(x);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
function primary() {
|
||||
if (k >= T.length) return 0;
|
||||
var t = T[k];
|
||||
if (t[0] === 'num') { k++; return t[1]; }
|
||||
if (t[0] === 'id') {
|
||||
var n = t[1]; k++;
|
||||
if (ist('(')) {
|
||||
k++; var a = [];
|
||||
if (!ist(')')) { a.push(ternary()); while (ist(',')) { k++; a.push(ternary()); } }
|
||||
if (ist(')')) k++;
|
||||
return fn(n, a);
|
||||
}
|
||||
return vars[n] !== undefined ? Number(vars[n]) : 0;
|
||||
}
|
||||
if (ist('(')) { k++; var r = ternary(); if (ist(')')) k++; return r; }
|
||||
k++; return 0;
|
||||
}
|
||||
function unary() { if (ist('-')) { k++; return -unary(); } if (ist('+')) { k++; return unary(); } return primary(); }
|
||||
function mul() { var l = unary(); while (ist('*') || ist('/')) { var o = T[k][1]; k++; var r = unary(); l = o === '*' ? l * r : (r === 0 ? 0 : l / r); } return l; }
|
||||
function add() { var l = mul(); while (ist('+') || ist('-')) { var o = T[k][1]; k++; var r = mul(); l = o === '+' ? l + r : l - r; } return l; }
|
||||
function cmp() {
|
||||
var l = add();
|
||||
while (k < T.length && T[k][0] === 'op' && ['>=', '<=', '==', '!=', '>', '<'].indexOf(T[k][1]) >= 0) {
|
||||
var o = T[k][1]; k++; var r = add();
|
||||
l = o === '>=' ? l >= r : o === '<=' ? l <= r : o === '>' ? l > r : o === '<' ? l < r : o === '==' ? l == r : l != r;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
function ternary() {
|
||||
var c = cmp();
|
||||
if (ist('?')) { k++; var a = ternary(); if (ist(':')) k++; var b = ternary(); return c ? a : b; }
|
||||
return c;
|
||||
}
|
||||
var out = Number(ternary());
|
||||
return isFinite(out) ? out : 1;
|
||||
}
|
||||
|
||||
function berechne(m, mitKuvert) {
|
||||
m = Math.max(1, parseInt(m, 10) || 1);
|
||||
var mult = par.norm > 0
|
||||
? formelEval(par.formel, { qty: m, menge: m, norm: par.norm, base_price: par.base, a: par.a, e: par.e })
|
||||
: 1;
|
||||
if (!isFinite(mult) || mult <= 0) mult = 1;
|
||||
var grund = par.base * m * mult;
|
||||
var besteMin = -1, bestePercent = 0;
|
||||
(par.tiers || []).forEach(function (t) {
|
||||
if (m >= t.min_qty && t.percent > 0 && t.min_qty > besteMin) { besteMin = t.min_qty; bestePercent = t.percent; }
|
||||
});
|
||||
if (bestePercent > 0) grund *= (1 - bestePercent / 100);
|
||||
var netto = grund / m;
|
||||
if (mitKuvert) netto += par.porto;
|
||||
return mitMwst ? netto * (1 + par.mwst / 100) : netto;
|
||||
}
|
||||
function fmt(v) { return v.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' €'; }
|
||||
function aktualisiere() { wert.textContent = fmt(berechne(menge.value, kuvert.checked)); }
|
||||
|
||||
menge.addEventListener('input', aktualisiere);
|
||||
kuvert.addEventListener('change', aktualisiere);
|
||||
// Erst beim Verlassen des Feldes auf 1 zurücksetzen – währenddessen darf es leer sein.
|
||||
menge.addEventListener('blur', function () {
|
||||
var n = parseInt(menge.value, 10);
|
||||
if (!(n >= 1)) { menge.value = '1'; aktualisiere(); }
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/** [skrift_preis_inkl produkt="briefe"] – Preis inkl. MwSt. */
|
||||
add_shortcode('skrift_preis_inkl', function ($atts) { return skrift_preis_render($atts, true); });
|
||||
|
||||
/** [skrift_preis_exkl produkt="briefe"] – Preis exkl. MwSt. */
|
||||
add_shortcode('skrift_preis_exkl', function ($atts) { return skrift_preis_render($atts, false); });
|
||||
|
||||
/** Alt-Shortcode bleibt gültig und zeigt den Bruttopreis. */
|
||||
add_shortcode('skrift_preis', function ($atts) { return skrift_preis_render($atts, true); });
|
||||
Reference in New Issue
Block a user