PageSourceSearch

https://www.personizer.com/wp-content/themes/personizer/assets/js/ref-calc.js

js personizer.com collected 2026-10-01 15:33:47 UTC 8,326 bytes, 222 lines download raw bytes

1(function () {
2    "use strict";
3
4    // Ref-calc runs ONLY from injected pricing:
5    // window.PZ_PRICING_RAW = { "time-tracking": {monthly:123, yearly:111, twoYear:99}, ... }
6    // Values are CENTS per employee per month (yearly/twoYear already divided by 12/24 in PHP).
7
8    const DEFAULTS = {
9        commissionRate: 0.20,   // 20% Provision auf NETTO-Umsatz
10        introDiscount: 0.10,    // 10% Rabatt in der Einführungsphase
11        maxEmployees: 400,      // set to 200 if you want your old cap back
12        addMoreThanMaxOption: true, // adds "Mehr als X" option in the dropdown
13    };
14
15    const fmtEUR = new Intl.NumberFormat("de-DE", { style: "currency", currency: "EUR" });
16
17    // Kept for robustness (in case any value ever comes as "2,50 €")
18    function parseEURToCents(str) {
19        if (typeof str !== "string") return 0;
20        let s = str.replace(/\u00A0/g, " ").trim();
21        s = s.replace(/€/g, "").replace(/\s/g, "");
22        s = s.replace(/\./g, "");
23        s = s.replace(/,/g, ".");
24        const v = Number.parseFloat(s);
25        return Number.isFinite(v) ? Math.round(v * 100) : 0;
26    }
27
28    function toCents(v) {
29        if (typeof v === "number" && Number.isFinite(v)) return Math.round(v);
30        if (typeof v === "string") return parseEURToCents(v);
31        return 0;
32    }
33
34    function centsToEUR(cents) {
35        return fmtEUR.format((cents || 0) / 100);
36    }
37
38    function planKeyForTermMonths(termMonths) {
39        if (termMonths >
39= 24) return "twoYear";
40        if (termMonths >= 12) return "yearly";
41        return "monthly";
42    }
43
44    // Einführungsphase (Rabatt + “erste X Monate” Label):
45    // - Monatlich: immer 12 Monate
46    // - Jährlich: 12 Monate
47    // - 2 Jahre: 24 Monate
48    // If you want ALWAYS 12 months intro, change to: return 12;
49    function introMonthsForTerm(termMonths) {
50        if (termMonths <= 1) return 12;
51        return termMonths;
52    }
53
54    function monthsLabelFirst(introMonths) {
55        if (introMonths <= 1) return "Die ersten 1 Monat";
56        return `Die ersten ${introMonths} Monate`;
57    }
58
59    function monthsLabelAfter(introMonths) {
60        if (introMonths <= 1) return "Ab dem 2. Monat";
61        return `Ab dem ${introMonths + 1}. Monat`;
62    }
63
64    function showError(el, msg) {
65        if (!el) return;
66        el.textContent = msg || "";
67        el.style.display = msg ? "block" : "none";
68    }
69
70    function setDashes(outFirst, outAfter, outYear) {
71        if (outFirst) outFirst.textContent = "–";
72        if (outAfter) outAfter.textContent = "–";
73        if (outYear) outYear.textContent = "–";
74    }
75
76    function loadPricingFromWindow() {
77        const pricing = window.PZ_PRICING_RAW;
78        if (!pricing || typeof pricing !== "object") {
79            throw new Error("Pricing missing (window.PZ_PRICING_RAW not injected).");
80        }
81        return pricing;
82    }
83
84    // Provision (Bestandskunde):
85    // - Basis: NETTO-Umsatz des Neukunden (ohne MwSt)
86    // - Einführungsphase: introDiscount Rabatt auf Neukundenpreis
87    // - Danach: ohne Rabatt
88    function calc(pricing, employees, termMonths, selectedModules, cfg) {
89        const planKey = planKeyForTermMonths(termMonths);
90        const introMonths = introMonthsForTerm(termMonths);
91
92        let netPerEmpCents = 0;
93        for (const modKey of selectedModules) {
94            const mod = pricing?.[modKey];
95            if (!mod) continue;
96            // With injected pricing, this is already CENTS per emp per month
97            netPerEmpCents += toCents(mod[planKey]);
98        }
99
100        // NETTO-Preis pro Monat (alle MA)
101        const netFullCents = netPerEmpCents * employees;
102
103        // NETTO-Preis pro Monat in der Einführungsphase (Rabatt)
104        const netIntroCents = Math.round(netFullCents * (1 - cfg.introDiscount));
105
106        // Provision pro Monat
107        const commTermCents = Math.round(netIntroCents * cfg.commissionRate); // Einführungsphase
108        const commAfterCents = Math.round(netFullCents * cfg.commissionRate); // danach
109
110        // Jahreswert (erste 12 Monate ab Start)
111        const mIntroInYear1 = Math.min(12, introMonths);
112        const mAfterInYear1 = 12 - mIntroInYear1;
113        const totalYearCents = (commTermCents * mIntroInYear1) + (commAfterCents * mAfterInYear1);
114
115        return { commTermCents, commAfterCents, totalYearCents, introMonths };
116    }
117
118    function initInstance(root) {
119        const cfg = { ...DEFAULTS };
120
121        const elEmployees = root.querySelector("#pz-affcalc-employees");
122        const elTerm = root.querySelector("#pz-affcalc-term");
123        const elChecks = Array.from(root.querySelectorAll(".pz-affcalc__check input[type='checkbox']"));
124
125        const outFirst = root.querySelector("[data-out-first]");
126        const outAfter = root.querySelector("[data-out-after]");
127        const outYear = root.querySelector("[data-out-year]");
128        const lblFirst = root.querySelector("[data-label-first]");
129        const lblAfter = root.querySelector("[data-label-after]");
130        const elError = root.querySelector("[data-error]");
131
132        function populateEmployees() {
133            if (!elEmployees) return;
134
135            const frag = document.createDocumentFragment();
136
137            for (let i = 5; i <= cfg.maxEmployees; i += 5) {
138                const opt = document.createElement("option");
139                opt.value = String(i);
140                opt.textContent = String(i);
141                frag.appendChild(opt);
142            }
143
144            if (cfg.addMoreThanMaxOption) {
145                const optPlus = document.createElement("option");
146                optPlus.value = String(cfg.maxEmployees + 1);
147                optPlus.textContent = `Mehr als ${cfg.maxEmployees}`;
148                frag.appendChild(optPlus);
149            }
150
151            elEmployees.innerHTML = "";
152            elEmployees.appendChild(frag);
153            elEmployees.value = "15";
154        }
155
156        function selectedModules() {
157            return elChecks.filter((c) => c.checked).map((c) => c.value);
158        }
159
160        function render(pricing) {
161            const employees = Math.max(1, parseInt(elEmployees?.value || "1", 10) || 1);
162            const termMonths = Math.max(1, parseInt(elTerm?.value || "12", 10) || 12);
163            const sel = selectedModules();
164
165            if (!sel.length) {
166                showError(elError, "Bitte wähle mindestens ein Modul aus.");
167                setDashes(outFirst, outAfter, outYear);
168                return;
169            }
170
171            if (employees > cfg.maxEmployees) {
172                showError(elError, `Für mehr als ${cfg.maxEmployees} Mitarbeitende ist der Preis auf Anfrage.`);
173                setDashes(outFirst, outAfter, outYear);
174                return;
175            }
176
177            showError(elError, "");
178
179            const { commTermCents, commAfterCents, totalYearCents, introMonths } =
180                calc(pricing, employees, termMonths, sel, cfg);
181
182            if (lblFirst) lblFirst.textContent = monthsLabelFirst(introMonths);
183            if (lblAfter) lblAfter.textContent = monthsLabelAfter(introMonths);
184
185            if (outFirst) outFirst.textContent = centsToEUR(commTermCents);
186            if (outAfter) outAfter.textContent = centsToEUR(commAfterCents);
187            if (outYear) outYear.textContent = centsToEUR(totalYearCents);
188        }
189
190        function boot() {
191            try {
192                showError(elError, "");
193                populateEmployees();
194
195                const pricing = loadPricingFromWindow();
196
197                const rerender = () => render(pricing);
198                elEmployees?.addEventListener("change", rerender);
199                elTerm?.addEventListener("change", rerender);
200                elChecks.forEach((c) => c.addEventListener("change", rerender));
201
202                rerender();
203            } catch (err) {
204                console.error(err);
205                showError(elError, `Hinweis: ${err?.message || "Unbe
205kannter Fehler"}`);
206                setDashes(outFirst, outAfter, outYear);
207            }
208        }
209
210        boot();
211    }
212
213    function initAll() {
214        document.querySelectorAll("[data-pz-affcalc]").forEach(initInstance);
215    }
216
217    if (document.readyState === "loading") {
218        document.addEventListener("DOMContentLoaded", initAll);
219    } else {
220        initAll();
221    }
222})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.