Last active
November 7, 2025 16:29
-
-
Save Windowsfreak/1dbff9f948f3629a1613b5f7d10ead74 to your computer and use it in GitHub Desktop.
Aurum-Verdienstrechner mit jährlicher Steuerauswertung
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (function(){ | |
| let targetFinal = { | |
| main: 0, | |
| affiliate: 0, | |
| invest: 0 | |
| }; | |
| let hideCreditCard = false; | |
| let correctionsText = ""; | |
| class AurumCryptor { | |
| #password; | |
| #iterations; | |
| #encoder; | |
| #decoder; | |
| constructor(password, iterations = 150000) { | |
| if (!password) { | |
| throw new Error("Password is required for key derivation"); | |
| } | |
| this.#password = password; | |
| this.#iterations = iterations; | |
| this.#encoder = new TextEncoder(); | |
| this.#decoder = new TextDecoder(); | |
| } | |
| async deriveKey(salt) { | |
| const baseKey = await window.crypto.subtle.importKey( | |
| "raw", | |
| this.#encoder.encode(this.#password), | |
| "PBKDF2", | |
| false, | |
| ["deriveKey"] | |
| ); | |
| return window.crypto.subtle.deriveKey( | |
| { | |
| name: "PBKDF2", | |
| salt: salt, | |
| iterations: this.#iterations, | |
| hash: "SHA-256" | |
| }, | |
| baseKey, | |
| { | |
| name: "AES-GCM", | |
| length: 256 | |
| }, | |
| false, | |
| ["encrypt", "decrypt"] | |
| ); | |
| } | |
| async decode(encryptedBase64) { | |
| const encryptedBytes = Uint8Array.from(atob(encryptedBase64), (char) => char.charCodeAt(0)); | |
| const salt = encryptedBytes.slice(0, 16); | |
| const iv = encryptedBytes.slice(16, 28); | |
| const ciphertext = encryptedBytes.slice(28); | |
| const aesKey = await this.deriveKey(salt); | |
| const decryptedBytes = await window.crypto.subtle.decrypt( | |
| { | |
| name: "AES-GCM", | |
| iv: iv | |
| }, | |
| aesKey, | |
| ciphertext | |
| ); | |
| return this.#decoder.decode(decryptedBytes); | |
| } | |
| async encode(data) { | |
| const dataStr = JSON.stringify(data); | |
| const salt = window.crypto.getRandomValues(new Uint8Array(16)); | |
| const iv = window.crypto.getRandomValues(new Uint8Array(12)); | |
| const aesKey = await this.deriveKey(salt); | |
| const encrypted = await window.crypto.subtle.encrypt( | |
| { | |
| name: "AES-GCM", | |
| iv: iv | |
| }, | |
| aesKey, | |
| this.#encoder.encode(dataStr) | |
| ); | |
| const combined = new Uint8Array(salt.length + iv.length + encrypted.byteLength); | |
| combined.set(salt, 0); | |
| combined.set(iv, salt.length); | |
| combined.set(new Uint8Array(encrypted), salt.length + iv.length); | |
| return btoa(String.fromCharCode(...combined)); | |
| } | |
| } | |
| const cryptor = new AurumCryptor("default-password"); | |
| async function renderForm() { | |
| const section = document.getElementsByTagName("section")[0]; | |
| const token = localStorage.getItem("token"); | |
| if (!token) { | |
| section.innerHTML = `<b>Fehler:</b> No token found in localStorage.`; | |
| return; | |
| } | |
| const headers = { | |
| "Accept": "application/json", | |
| "Authorization": `Bearer ${token}` | |
| }; | |
| try { | |
| const mainResp = await fetch('https://api.aurum.foundation/', { headers }); | |
| let mainRaw = await mainResp.json(); | |
| let mainData; | |
| if (mainRaw.encrypted) { | |
| const decryptedMainStr = await cryptor.decode(mainRaw.encrypted); | |
| mainData = JSON.parse(decryptedMainStr); | |
| } else { | |
| mainData = mainRaw; | |
| } | |
| targetFinal.main = parseFloat(mainData.balance?.total.replace(' ', '') || '0'); | |
| const partnerResp = await fetch('https://api.aurum.foundation/partners', { headers }); | |
| let partnerRaw = await partnerResp.json(); | |
| let partnerData; | |
| if (partnerRaw.encrypted) { | |
| const decryptedPartnerStr = await cryptor.decode(partnerRaw.encrypted); | |
| partnerData = JSON.parse(decryptedPartnerStr); | |
| } else { | |
| partnerData = partnerRaw; | |
| } | |
| targetFinal.affiliate = parseFloat(partnerData.partnerBalance.replace(' ', '') || '0'); | |
| const investResp = await fetch('https://api.aurum.foundation/investments', { headers }); | |
| let investRaw = await investResp.json(); | |
| let investData; | |
| if (investRaw.encrypted) { | |
| const decryptedInvestStr = await cryptor.decode(investRaw.encrypted); | |
| investData = JSON.parse(decryptedInvestStr); | |
| } else { | |
| investData = investRaw; | |
| } | |
| const deposit = parseFloat(investData.balance?.totalDeposit.replace(' ', '') || '0'); | |
| const tokenBal = parseFloat(investData.balance?.tokenBalance.replace(' ', '') || '0'); | |
| targetFinal.invest = deposit + tokenBal; | |
| } catch (e) { | |
| console.error('Failed to fetch balances:', e); | |
| targetFinal = { main: 0, affiliate: 0, invest: 0 }; | |
| } | |
| section.innerHTML = ` | |
| <form id="configForm"> | |
| <label>Main Balance: <input type="number" step="0.01" name="main" value="${targetFinal.main.toFixed(2)}" required class="min-h-12 w-full text-[17px] rounded-[12px] border-2 bg-bg-color-input py-2 px-3 text-text-color-primary outline-hidden placeholder:text-text-color-placeholder pr-[145px]" placeholder="0.00"></label><br> | |
| <label>Affiliate Balance: <input type="number" step="0.01" name="affiliate" value="${targetFinal.affiliate.toFixed(2)}" required class="min-h-12 w-full text-[17px] rounded-[12px] border-2 bg-bg-color-input py-2 px-3 text-text-color-primary outline-hidden placeholder:text-text-color-placeholder pr-[145px]" placeholder="0.00"></label><br> | |
| <label>Invest Balance: <input type="number" step="0.01" name="invest" value="${targetFinal.invest.toFixed(2)}" required class="min-h-12 w-full text-[17px] rounded-[12px] border-2 bg-bg-color-input py-2 px-3 text-text-color-primary outline-hidden placeholder:text-text-color-placeholder pr-[145px]" placeholder="0.00"></label><br> | |
| <label>Positionen korrigieren:<br><textarea name="corrections" rows="4" cols="50" class="min-h-24 w-full text-[17px] rounded-[12px] border-2 bg-bg-color-input py-2 px-3 text-text-color-primary outline-hidden placeholder:text-text-color-placeholder" placeholder="Beispiel:\n2020-06-25 16:18\n2020-06-26 17:30 DIVIDEND 420.69"></textarea></label><br> | |
| <label><input type="checkbox" name="hideCreditCard"> Kreditkarte verstecken</label><br> | |
| <button type="submit" id="submitBtn" class="text-sm flex gap-2 items-center font-medium font-geologica justify-center rounded-[12px] px-4 py-2.5 transition duration-300 ease-in-out focus:outline-hidden bg-bg-color-main-theme-deep text-white md:hover:bg-bg-color-main-theme-dark">Bestätigen und Bericht generieren</button> | |
| </form> | |
| `; | |
| const form = document.getElementById("configForm"); | |
| form.addEventListener("submit", async (e) => { | |
| e.preventDefault(); | |
| // Disable button and show loading state | |
| const submitBtn = document.getElementById("submitBtn"); | |
| submitBtn.disabled = true; | |
| submitBtn.className = "hover:cursor-not-allowed bg-bg-color-input hover:bg-bg-color-input text-text-color-placeholder text-sm flex gap-2 items-center font-medium font-geologica justify-center rounded-[12px] px-4 py-2.5 transition duration-300 ease-in-out focus:outline-hidden"; | |
| submitBtn.textContent = "Transaktionen werden abgerufen... 0%"; | |
| // Remove menu for full screen view | |
| const menu = document.getElementsByTagName('aside'); | |
| if (menu.length > 0) | |
| menu[0].parentNode.remove(); | |
| const formData = new FormData(form); | |
| targetFinal = { | |
| main: parseFloat(formData.get("main")), | |
| affiliate: parseFloat(formData.get("affiliate")), | |
| invest: parseFloat(formData.get("invest")) | |
| }; | |
| hideCreditCard = formData.get("hideCreditCard") === "on"; | |
| correctionsText = formData.get("corrections") || ""; | |
| try { | |
| const payments = await fetchAllPayments(); | |
| const history = buildHistory(payments, targetFinal); | |
| section.innerHTML = renderTable(history) + renderFinancialReport(history); | |
| const lastRow = history[history.length - 1]; | |
| function approxEqual(a, b, t = 0.001) { return Math.abs(a - b) < t; } | |
| if (approxEqual(lastRow.main, targetFinal.main) && | |
| approxEqual(lastRow.invest, targetFinal.invest) && | |
| approxEqual(lastRow.affiliate, targetFinal.affiliate)) { | |
| section.insertAdjacentHTML('beforeend', `<p style="color:green; font-weight:bold;">Abrechnung stimmt auf den Cent genau.</p>`); | |
| } else { | |
| section.insertAdjacentHTML('beforeend', `<p style="color:red; font-weight:bold;">Es gibt Abweichungen mit dem Endergebnis.</p>`); | |
| section.insertAdjacentHTML('beforeend', ` | |
| <table class="w-full mt-2" style="border-collapse: collapse;"> | |
| <thead> | |
| <tr> | |
| <th class="py-2 px-2 text-left">Konto</th> | |
| <th class="py-2 px-2 text-right">Soll</th> | |
| <th class="py-2 px-2 text-right">Ist</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td>Main</td> | |
| <td class="text-right">${lastRow.main.toFixed(2)}</td> | |
| <td class="text-right">${targetFinal.main.toFixed(2)}</td> | |
| </tr> | |
| <tr> | |
| <td>Invest</td> | |
| <td class="text-right">${lastRow.invest.toFixed(2)}</td> | |
| <td class="text-right">${targetFinal.invest.toFixed(2)}</td> | |
| </tr> | |
| <tr> | |
| <td>Affiliate</td> | |
| <td class="text-right">${lastRow.affiliate.toFixed(2)}</td> | |
| <td class="text-right">${targetFinal.affiliate.toFixed(2)}</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| `); | |
| } | |
| } catch (e) { | |
| section.innerHTML = `<b>Fehler:</b> ${e.message}`; | |
| } | |
| }); | |
| } | |
| async function fetchAllPayments() { | |
| const token = localStorage.getItem("token"); | |
| if (!token) throw new Error("No token found in localStorage."); | |
| const headers = { | |
| "Accept": "application/json", | |
| "Authorization": `Bearer ${token}` | |
| }; | |
| let all = []; | |
| for (let page = 0; ; ++page) { | |
| const resp = await fetch(`https://api.aurum.foundation/payments?limit=30&page=${page}`, { headers }); | |
| if (!resp.ok) throw new Error("Fetch failed: " + resp.status); | |
| let jsonRaw = await resp.json(); | |
| let json; | |
| if (jsonRaw.encrypted) { | |
| const decryptedJsonStr = await cryptor.decode(jsonRaw.encrypted); | |
| json = JSON.parse(decryptedJsonStr); | |
| } else { | |
| json = jsonRaw; | |
| } | |
| // Update progress in button | |
| const submitBtn = document.getElementById("submitBtn"); | |
| if (submitBtn && json.pages > 0) { | |
| const progress = Math.round(((page + 1) / json.pages) * 100); | |
| submitBtn.textContent = `Transaktionen werden abgerufen... ${progress}%`; | |
| } | |
| all = all.concat(json.payments || []); | |
| if (json.page >= json.pages) break; | |
| } | |
| // Filter out unsupported currencies | |
| all = all.filter(tx => { | |
| const supportedTickers = ['USDT', 'USDC']; | |
| const tickerOk = !tx.ticker || supportedTickers.includes(tx.ticker); | |
| const cryptoTickerOk = !tx.cryptoTicker || supportedTickers.includes(tx.cryptoTicker); | |
| return tickerOk && cryptoTickerOk; | |
| }); | |
| // Filter out rejected card recharges | |
| all = all.filter(tx => !(tx.kind === "CARD_RECHARGE" && tx.statusName === "transactions:REJECTED")); | |
| return all; | |
| } | |
| function buildHistory(payments, targetBalances) { | |
| // Parse corrections from textarea | |
| const corrections = parseCorrections(correctionsText); | |
| // Sort payments chronologically (oldest first) | |
| const paymentsChrono = [...payments].sort((a, b) => new Date(a.date) - new Date(b.date)); | |
| // Apply corrections - filter out unwanted entries and add synthetic ones | |
| let filteredPayments = []; | |
| for (const tx of paymentsChrono) { | |
| const correction = corrections.find(c => c.date === tx.date); | |
| if (correction && !correction.synthetic) { | |
| // Skip this transaction | |
| continue; | |
| } | |
| filteredPayments.push(tx); | |
| } | |
| // Add synthetic transactions | |
| for (const correction of corrections) { | |
| if (correction.synthetic) { | |
| const syntheticTx = { | |
| date: correction.date, | |
| kind: correction.kind, | |
| amount: correction.amount.toString(), | |
| }; | |
| // Insert in correct chronological position | |
| const insertIndex = filteredPayments.findIndex(tx => new Date(tx.date) > new Date(correction.date)); | |
| if (insertIndex === -1) { | |
| filteredPayments.push(syntheticTx); | |
| } else { | |
| filteredPayments.splice(insertIndex, 0, syntheticTx); | |
| } | |
| } | |
| } | |
| const paymentsReversed = [...filteredPayments].reverse(); | |
| let reversedState = { | |
| main: targetBalances.main, | |
| invest: targetBalances.invest, | |
| affiliate: targetBalances.affiliate, | |
| card: 0, | |
| profit: 0, | |
| provision: 0, | |
| totalCosts: 0 | |
| }; | |
| let totalProfit = 0; | |
| let totalProvision = 0; | |
| for (let i = paymentsReversed.length - 1; i >= 0; --i) { | |
| const tx = paymentsReversed[i]; | |
| const kind = tx.kind; | |
| const amt = getTransactionAmount(tx); | |
| switch (kind) { | |
| case "REPLENISHMENT": reversedState.main -= amt; break; | |
| case "WITHDRAWAL": reversedState.main += amt; break; | |
| case "SUBSCRIPTION": | |
| case "LICENSE": reversedState.main += amt; break; | |
| case "INVESTMENT": reversedState.main += amt; reversedState.invest -= amt; break; | |
| case "TOP_UP_DEPOSIT": reversedState.main += amt; reversedState.invest -= amt; break; | |
| case "REINVESTMENT": reversedState.invest -= amt; totalProfit += amt; break; | |
| case "DIVIDEND": reversedState.invest -= amt; totalProfit += amt; break; | |
| case "CLAIMED_DIVIDEND": reversedState.main -= amt; reversedState.invest += amt; break; | |
| case "REFERRAL_FUND": | |
| case "TEAM_EARNINGS": | |
| case "REFERRAL_LICENSE_FUND": | |
| case "REFERRAL_CARD_PURCHASE": | |
| case "REFERRAL_CARD_TOPUP": | |
| case "TEAM_EARNING_LIVE_TRADING": | |
| case "SHAREHOLDER_BONUS": reversedState.affiliate -= amt; totalProvision += amt; break; | |
| case "REFERRAL_RANK_BONUS": reversedState.main -= amt; totalProvision += amt; break; | |
| case "TRANSFER": | |
| if ((tx.asset === "PARTNER-USDT" && tx.targetAsset === "MAIN-USDT") || (!tx.asset && !tx.targetAsset)) { | |
| reversedState.affiliate += amt; | |
| reversedState.main -= amt; | |
| } | |
| break; | |
| case "CARD_RECHARGE": reversedState.main += amt; break; | |
| case "CARD_PURCHASE": | |
| case "CARD_PURCHASE_PHYSICAL": reversedState.main += amt; break; | |
| } | |
| } | |
| let history = [ | |
| { | |
| date: "(Initial)", | |
| kind: "-", | |
| amount: 0, | |
| main: 0, | |
| invest: 0, | |
| affiliate: 0, | |
| card: 0, | |
| sum: 0, | |
| profit: 0, | |
| provision: 0, | |
| costs: 0, | |
| totalCosts: 0 | |
| } | |
| ]; | |
| let main = 0; | |
| let invest = 0; | |
| let affiliate = 0; | |
| let card = 0; | |
| let profit = 0; | |
| let provision = 0; | |
| let totalCosts = 0; | |
| filteredPayments.forEach(tx => { | |
| let amount = getTransactionAmount(tx); | |
| let kind = tx.kind; | |
| let costAddition = 0; | |
| if (hideCreditCard && (kind === "CARD_RECHARGE" || kind === "CARD_PURCHASE" || kind === "CARD_PURCHASE_PHYSICAL")) { | |
| kind = "MAINTENANCE_FEE"; | |
| main -= amount; | |
| costAddition = amount; | |
| } else { | |
| switch (kind) { | |
| case "REPLENISHMENT": main += amount; break; | |
| case "WITHDRAWAL": | |
| main -= amount; | |
| // Fix AURUM Bug: Calculate correct withdrawal fees | |
| costAddition = (amount * 0.01) + 1.01; | |
| break; | |
| case "SUBSCRIPTION": | |
| case "LICENSE": | |
| main -= amount; | |
| costAddition = amount; | |
| break; | |
| case "INVESTMENT": | |
| // Fix AURUM Bug: Calculate correct initial investment amount | |
| if (tx.kindName) { | |
| amount += reversedState.invest; | |
| } | |
| invest += amount; | |
| main -= amount; | |
| break; | |
| case "TOP_UP_DEPOSIT": | |
| main -= amount; | |
| invest += amount; | |
| break; | |
| case "DIVIDEND": | |
| invest += amount; | |
| profit += amount; | |
| break; | |
| case "REINVESTMENT": | |
| invest += amount; | |
| profit += amount; | |
| break; | |
| case "CLAIMED_DIVIDEND": | |
| main += amount; | |
| invest -= amount; | |
| break; | |
| case "REFERRAL_FUND": | |
| case "TEAM_EARNINGS": | |
| case "REFERRAL_LICENSE_FUND": | |
| case "REFERRAL_CARD_PURCHASE": | |
| case "REFERRAL_CARD_TOPUP": | |
| case "TEAM_EARNING_LIVE_TRADING": | |
| case "SHAREHOLDER_BONUS": | |
| affiliate += amount; | |
| provision += amount; | |
| break; | |
| case "REFERRAL_RANK_BONUS": | |
| main += amount; | |
| provision += amount; | |
| break; | |
| case "TRANSFER": | |
| if ((tx.asset === "PARTNER-USDT" && tx.targetAsset === "MAIN-USDT") || (!tx.asset && !tx.targetAsset)) { | |
| affiliate -= amount; | |
| main += amount; | |
| } | |
| break; | |
| case "CARD_RECHARGE": | |
| // Fix AURUM Bug: Calculate correct card fees | |
| main -= amount; | |
| const fee = amount * 0.022; | |
| const effective = amount - fee; | |
| card += effective; | |
| costAddition = fee; | |
| break; | |
| case "CARD_PURCHASE": | |
| case "CARD_PURCHASE_PHYSICAL": | |
| main -= amount; | |
| costAddition = amount; | |
| break; | |
| } | |
| } | |
| totalCosts += costAddition; | |
| history.push({ | |
| date: tx.date, | |
| kind: kind, | |
| amount: amount, | |
| main: main, | |
| invest: invest, | |
| affiliate: affiliate, | |
| card: card, | |
| sum: main + invest + affiliate, | |
| profit: profit, | |
| provision: provision, | |
| costs: costAddition, | |
| totalCosts: totalCosts | |
| }); | |
| }); | |
| return history; | |
| } | |
| function getTransactionAmount(tx) { | |
| // If both cryptoAmount and amount are set, trust cryptoAmount | |
| if (tx.cryptoAmount && tx.amount) { | |
| return parseFloat(tx.cryptoAmount); | |
| } | |
| return parseFloat(tx.amount || '0'); | |
| } | |
| function parseCorrections(correctionsText) { | |
| if (!correctionsText.trim()) return []; | |
| const lines = correctionsText.split('\n').filter(line => line.trim()); | |
| const corrections = []; | |
| for (const line of lines) { | |
| const parts = line.trim().split(' '); | |
| if (parts.length >= 2) { | |
| const date = parts[0] + ' ' + parts[1]; | |
| if (parts.length > 2) { | |
| // Synthetic transaction | |
| const kind = parts[2]; | |
| const amount = parseFloat(parts[3] || '0'); | |
| corrections.push({ | |
| date: date, | |
| synthetic: true, | |
| kind: kind, | |
| amount: amount | |
| }); | |
| } else { | |
| // Filter out existing transaction | |
| corrections.push({ | |
| date: date, | |
| synthetic: false | |
| }); | |
| } | |
| } | |
| } | |
| return corrections; | |
| } | |
| function translateKind(kind) { | |
| const map = { | |
| "REPLENISHMENT": "Eingezahlt", | |
| "WITHDRAWAL": "Ausgezahlt", | |
| "WITHDRAWAL_DEPOSIT": "Ausgezahlt (Deposit)", | |
| "INVESTMENT": "Invest", | |
| "TOP_UP_DEPOSIT": "Invest", | |
| "CARD_RECHARGE": "Karte aufladen", | |
| "SUBSCRIPTION": "Abonnement", | |
| "REINVESTMENT": "Reinvest", | |
| "DIVIDEND": "Dividende", | |
| "CLAIMED_DIVIDEND": "Umbuchung", | |
| "REFERRAL_FUND": "Empfehlungsprovision", | |
| "TEAM_EARNINGS": "Teameinnahmen", | |
| "REFERRAL_RANK_BONUS": "Rangbonus", | |
| "TRANSFER": "Übertragung", | |
| "CARD_PURCHASE": "Kartenbestellung", | |
| "CARD_PURCHASE_PHYSICAL": "Physische Karte", | |
| "MAINTENANCE_FEE": "Verwaltungsgebühren", | |
| "LICENSE": "Lizenz", | |
| "FLASH_LOAN_PROFIT": "Flash Loan Profit (nicht unterstützt)", | |
| "LIVE_TRADING": "Live Trading (nicht unterstützt)", | |
| "LIVE_TRADING_BOTS": "Live Trading Bots (nicht unterstützt)", | |
| "LIVE_TRADING_BOT_STOP": "Live Trading Bot Stop (nicht unterstützt)", | |
| "TEAM_EARNING_LIVE_TRADING": "Team Live Trading", | |
| "AURUM_TOKEN": "Aurum Token (nicht unterstützt)", | |
| "REFERRAL_LICENSE_FUND": "Empfehlungs-Lizenz-Fonds", | |
| "REFERRAL_CARD_PURCHASE": "Empfehlungs-Kartenkauf", | |
| "REFERRAL_CARD_TOPUP": "Empfehlungs-Kartenaufladung", | |
| "CARD_PURCHASE_CASHBACK": "Karten-Cashback (nicht unterstützt)", | |
| "SHAREHOLDER_BONUS": "Aktionärsbonus" | |
| }; | |
| return map[kind] || kind; | |
| } | |
| function renderTable(rows) { | |
| let html = `<table class="w-full" style="border-collapse: collapse;">` + | |
| `<thead><tr>` + | |
| ['Zeitpunkt', 'Vorgang', 'Betrag', 'Main', 'Invest', 'Affiliate', 'Gesamt', 'Profit', 'Provision', 'Gebühren', 'Gesamtkosten'] | |
| .map(h => `<th class="py-3 text-xs px-2 pb-5 pt-2 text-left font-light text-text-color-secondary">${h}</th>`).join('') + | |
| `</tr></thead><tbody>`; | |
| for (const row of rows) { | |
| html += `<tr class="border-b border-border-color-primary">` + | |
| `<td class="text-[14px] font-light text-text-color-primary">${row.date}</td>` + | |
| `<td class="text-[14px] font-light text-text-color-primary">${translateKind(row.kind)}</td>` + | |
| `<td class="text-[14px] font-light text-text-color-primary text-right">${row.amount != null ? Number(row.amount).toFixed(2).replace('-0', '0') : ''}</td>` + | |
| `<td class="text-[14px] font-light text-text-color-primary text-right">${row.main.toFixed(2).replace('-0', '0')}</td>` + | |
| `<td class="text-[14px] font-light text-text-color-primary text-right">${row.invest.toFixed(2).replace('-0', '0')}</td>` + | |
| `<td class="text-[14px] font-light text-text-color-primary text-right">${row.affiliate.toFixed(2).replace('-0', '0')}</td>` + | |
| `<td class="text-[14px] font-light text-text-color-primary text-right">${row.sum.toFixed(2).replace('-0', '0')}</td>` + | |
| `<td class="text-[14px] font-light text-text-color-primary text-right">${row.profit.toFixed(2).replace('-0', '0')}</td>` + | |
| `<td class="text-[14px] font-light text-text-color-primary text-right">${row.provision.toFixed(2).replace('-0', '0')}</td>` + | |
| `<td class="text-[14px] font-light text-text-color-primary text-right">${row.costs.toFixed(2).replace('-0', '0')}</td>` + | |
| `<td class="text-[14px] font-light text-text-color-primary text-right">${row.totalCosts.toFixed(2).replace('-0', '0')}</td>` + | |
| `</tr>`; | |
| } | |
| html += '</tbody></table>'; | |
| return html; | |
| } | |
| function renderFinancialReport(rows) { | |
| const years = {}; | |
| rows.forEach(row => { | |
| if (row.date.startsWith('(')) return; | |
| const yearMatch = row.date.match(/^(\d{4})/); | |
| if (yearMatch) { | |
| const year = yearMatch[1]; | |
| if (!years[year]) years[year] = []; | |
| years[year].push(row); | |
| } | |
| }); | |
| const sortedYears = Object.keys(years).sort((a, b) => a - b); | |
| let html = ''; | |
| sortedYears.forEach(year => { | |
| const yearRows = years[year]; | |
| if (yearRows.length === 0) return; | |
| let prevRow = rows[0]; | |
| const prevYearIndex = sortedYears.indexOf(year) - 1; | |
| if (prevYearIndex >= 0) { | |
| const prevYearRows = years[sortedYears[prevYearIndex]]; | |
| prevRow = prevYearRows[prevYearRows.length - 1]; | |
| } | |
| const endRow = yearRows[yearRows.length - 1]; | |
| const diffProfit = endRow.profit - prevRow.profit; | |
| const diffProvision = endRow.provision - prevRow.provision; | |
| const diffCosts = endRow.totalCosts - prevRow.totalCosts; | |
| // Calculate category breakdown | |
| const categoryBreakdown = calculateCategoryBreakdown(yearRows, prevRow); | |
| const net = diffProfit + diffProvision - diffCosts; | |
| html += ` | |
| <div class="mt-4 mb-10"> | |
| <h3>Transaktionsaufstellung für den Zeitraum 01.01.-31.12.${year}</h3> | |
| ${renderCategoryTable(categoryBreakdown)} | |
| <h3>Ertragsaufstellung für den Zeitraum 01.01.-31.12.${year}</h3> | |
| <table class="w-full" style="border-collapse: collapse;"> | |
| <thead> | |
| <tr> | |
| <th class="py-3 text-l px-2 pb-5 pt-2 text-left font-light text-text-color-secondary" colspan="2">Höhe der ausländischen Kapitalerträge</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td class="text-[14px] font-light text-text-color-primary">Eigene Erträge</td> | |
| <td class="text-[14px] font-light text-text-color-primary text-right">${diffProfit.toFixed(2)} USDT</td> | |
| </tr> | |
| <tr> | |
| <td class="text-[14px] font-light text-text-color-primary">Erträge durch Partner (Provisionen)</td> | |
| <td class="text-[14px] font-light text-text-color-primary text-right">${diffProvision.toFixed(2)} USDT</td> | |
| </tr> | |
| <tr> | |
| <td class="text-[14px] font-light text-text-color-primary">Gebühren (Transaktionen, Netzwerk, System)</td> | |
| <td class="text-[14px] font-light text-text-color-primary text-right">${diffCosts.toFixed(2)} USDT</td> | |
| </tr> | |
| <tr> | |
| <td class="text-[14px] font-bold text-text-color-primary">Ausländische Kapitalerträge ohne Steuerabzug</td> | |
| <td class="text-[14px] font-bold text-text-color-primary text-right">${net.toFixed(2)} USDT</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| `; | |
| }); | |
| return html; | |
| } | |
| function calculateCategoryBreakdown(yearRows, prevRow) { | |
| const categories = {}; | |
| // Initialize all possible categories | |
| const allKinds = [ | |
| "REPLENISHMENT", "WITHDRAWAL", "WITHDRAWAL_DEPOSIT", "SUBSCRIPTION", "LICENSE", | |
| "INVESTMENT", "REINVESTMENT", "TOP_UP_DEPOSIT", "DIVIDEND", "CLAIMED_DIVIDEND", | |
| "AURUM_TOKEN", "FLASH_LOAN_PROFIT", "LIVE_TRADING", "LIVE_TRADING_BOTS", | |
| "LIVE_TRADING_BOT_STOP", "REFERRAL_FUND", "REFERRAL_RANK_BONUS", | |
| "TEAM_EARNINGS", "TEAM_EARNING_LIVE_TRADING", "REFERRAL_LICENSE_FUND", | |
| "CARD_RECHARGE", "REFERRAL_CARD_PURCHASE", "REFERRAL_CARD_TOPUP", | |
| "SHAREHOLDER_BONUS", "TRANSFER", "CARD_PURCHASE", "CARD_PURCHASE_PHYSICAL", | |
| "CARD_PURCHASE_CASHBACK", "MAINTENANCE_FEE" | |
| ]; | |
| allKinds.forEach(kind => { | |
| categories[kind] = { profit: 0, provision: 0, costs: 0 }; | |
| }); | |
| // Calculate differences for each transaction in the year | |
| for (let i = 0; i < yearRows.length; i++) { | |
| const currentRow = yearRows[i]; | |
| const previousRow = i === 0 ? prevRow : yearRows[i - 1]; | |
| let kind = currentRow.kind; | |
| if (kind === 'MAINTENANCE_FEE' || kind === 'REINVESTMENT') | |
| kind = 'DIVIDEND'; | |
| if (categories[kind]) { | |
| categories[kind].profit += currentRow.profit - previousRow.profit; | |
| categories[kind].provision += currentRow.provision - previousRow.provision; | |
| categories[kind].costs += currentRow.costs; | |
| } | |
| } | |
| return categories; | |
| } | |
| function renderCategoryTable(categories) { | |
| const hasNonZeroValues = Object.values(categories).some(cat => | |
| cat.profit !== 0 || cat.provision !== 0 || cat.costs !== 0 | |
| ); | |
| if (!hasNonZeroValues) return ''; | |
| let html = ` | |
| <table class="w-full mb-4" style="border-collapse: collapse;"> | |
| <thead> | |
| <tr> | |
| <th class="py-3 text-l px-2 pb-5 pt-2 text-left font-light text-text-color-secondary">Kategorie</th> | |
| <th class="py-3 text-l px-2 pb-5 pt-2 text-right font-light text-text-color-secondary">Profit</th> | |
| <th class="py-3 text-l px-2 pb-5 pt-2 text-right font-light text-text-color-secondary">Provision</th> | |
| <th class="py-3 text-l px-2 pb-5 pt-2 text-right font-light text-text-color-secondary">Kosten</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| `; | |
| Object.entries(categories).forEach(([kind, values]) => { | |
| if (values.profit !== 0 || values.provision !== 0 || values.costs !== 0) { | |
| html += ` | |
| <tr class="border-b border-border-color-primary"> | |
| <td class="text-[14px] font-light text-text-color-primary">${translateKind(kind)}</td> | |
| <td class="text-[14px] font-light text-text-color-primary text-right">${values.profit.toFixed(2)}</td> | |
| <td class="text-[14px] font-light text-text-color-primary text-right">${values.provision.toFixed(2)}</td> | |
| <td class="text-[14px] font-light text-text-color-primary text-right">${values.costs.toFixed(2)}</td> | |
| </tr> | |
| `; | |
| } | |
| }); | |
| html += '</tbody></table>'; | |
| return html; | |
| } | |
| renderForm(); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment