Feat: Rabattsystem in Rechnungserstellung integriert (Gründer/Referral)
- _get_discount_info() Hilfsfunktion in admin.py (Gründer 100%, Referral-Stufen 20/30/50%, von Gründer eingeladen 50%)
- list_upgrade_requests liefert discount_pct + discount_reason pro User
- GET /admin/users/{user_id}/discount Endpoint
- _handle_upgrade_invoices nutzt Rabatt für amount_net/discount_pct/after_disc + passende Notiz
- scheduler.py _create_renewal_invoice_draft: inline Rabattberechnung + korrekte Beträge
- admin.js: Discount-Badge in Upgrade-Card, data-Attribute am Invoice-Button, _discountNote(), discount_pct + notes im Modal vorbelegt
This commit is contained in:
parent
db4d5cb1b6
commit
2163169b73
3 changed files with 132 additions and 11 deletions
|
|
@ -3531,8 +3531,14 @@ window.Page_admin = (() => {
|
|||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-bottom:var(--space-2)">${_esc(r.email)}</div>
|
||||
<div style="display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap">
|
||||
${tierBadge(r.tier)}
|
||||
${r.discount_pct > 0 ? `<span style="display:inline-block;padding:1px 8px;border-radius:999px;
|
||||
font-size:11px;font-weight:700;background:#e67e22;color:#fff;margin-left:4px">
|
||||
${r.discount_pct}% Rabatt</span>` : ''}
|
||||
<span style="font-size:var(--text-xs);color:var(--c-text-muted)">${r.created_at?.slice(0,10) || ''}</span>
|
||||
</div>
|
||||
${r.discount_reason === 'founder' ? `<div style="font-size:10px;color:#e67e22;margin-top:2px">Gründer — kostenfrei</div>` : ''}
|
||||
${r.discount_reason === 'referred_by_founder' ? `<div style="font-size:10px;color:#e67e22;margin-top:2px">Von Gründer eingeladen</div>` : ''}
|
||||
${r.discount_reason === 'referral' ? `<div style="font-size:10px;color:var(--c-text-muted);margin-top:2px">${r.referral_count} Freunde geworben</div>` : ''}
|
||||
${r.message ? `<div style="margin-top:var(--space-2);font-size:var(--text-xs);color:var(--c-text-secondary);
|
||||
padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);
|
||||
background:var(--c-surface-raised,rgba(0,0,0,.04))">
|
||||
|
|
@ -3544,6 +3550,9 @@ window.Page_admin = (() => {
|
|||
<button class="btn adm-invoice-btn"
|
||||
data-name="${_esc(r.name)}" data-email="${_esc(r.email)}"
|
||||
data-tier="${r.tier}" data-address="${_esc(r.billing_address || '')}"
|
||||
data-discount="${r.discount_pct || 0}"
|
||||
data-discount-reason="${r.discount_reason || ''}"
|
||||
data-referral-count="${r.referral_count || 0}"
|
||||
style="background:#e67e22;color:#fff;border:none;
|
||||
padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);
|
||||
cursor:pointer;font-size:var(--text-sm);font-weight:600">
|
||||
|
|
@ -3632,9 +3641,20 @@ window.Page_admin = (() => {
|
|||
const _fmt = d => `${String(d.getDate()).padStart(2,'0')}.${String(d.getMonth()+1).padStart(2,'0')}.${d.getFullYear()}`;
|
||||
const _period = `${_fmt(_now)} – ${_fmt(_end)}`;
|
||||
|
||||
function _discountNote(reason, count, pct, tierLabel) {
|
||||
const agb = 'Jahresbeitrag gem. AGB. Bei vorzeitiger Kündigung keine anteilige Rückerstattung; Zugang bleibt bis Laufzeitende bestehen.';
|
||||
if (reason === 'founder') return `Gründer-Sonderkonditionen: ${tierLabel} kostenfrei als Dankeschön für deine Unterstützung als Gründer! ${agb}`;
|
||||
if (reason === 'referred_by_founder') return `Willkommen in der Gründer-Community! Als persönlich von einem Gründer eingeladenes Mitglied erhältst du dauerhaft ${pct}% Rabatt. ${agb}`;
|
||||
if (reason === 'referral') return `Herzlichen Dank für deine Unterstützung! Für ${count} geworbene Freunde erhältst du ${pct}% Rabatt. ${agb}`;
|
||||
return agb;
|
||||
}
|
||||
|
||||
el.querySelectorAll('.adm-invoice-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const { name, email, tier, address } = btn.dataset;
|
||||
const discountPct = Number(btn.dataset.discount) || 0;
|
||||
const discountReason = btn.dataset.discountReason || '';
|
||||
const referralCount = Number(btn.dataset.referralCount) || 0;
|
||||
const tierItem = TIER_ITEMS[tier] || { description: 'Ban Yaro Abo', unit_price: 0 };
|
||||
_openNeueRechnungModal(() => {
|
||||
_tab = 'rechnungen';
|
||||
|
|
@ -3644,8 +3664,9 @@ window.Page_admin = (() => {
|
|||
recipient_email: email,
|
||||
recipient_address: address || '',
|
||||
service_period: _period,
|
||||
discount_pct: discountPct,
|
||||
notes: _discountNote(discountReason, referralCount, discountPct, tierItem.description),
|
||||
items: [{ description: tierItem.description, quantity: 1, unit_price: tierItem.unit_price }],
|
||||
notes: 'Jahresbeitrag gem. AGB. Bei vorzeitiger Kündigung keine anteilige Rückerstattung; Zugang bleibt bis Laufzeitende bestehen.',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -3913,7 +3934,7 @@ window.Page_admin = (() => {
|
|||
<div style="display:grid;grid-template-columns:auto 1fr;gap:var(--space-3);align-items:center">
|
||||
<div style="display:flex;align-items:center;gap:var(--space-2)">
|
||||
<label class="form-label" style="font-size:var(--text-xs);margin:0;white-space:nowrap">Rabatt %</label>
|
||||
<input class="form-control" name="discount_pct" type="number" min="0" max="100" value="0"
|
||||
<input class="form-control" name="discount_pct" type="number" min="0" max="100" value="${p.discount_pct ?? 0}"
|
||||
style="width:80px" id="${id}-discount">
|
||||
</div>
|
||||
<!-- Live-Vorschau -->
|
||||
|
|
@ -3927,7 +3948,7 @@ window.Page_admin = (() => {
|
|||
<label class="form-label" style="font-size:var(--text-xs)">Notizen <span style="color:var(--c-text-muted)">(optional)</span></label>
|
||||
<textarea class="form-control" name="notes" rows="2"
|
||||
style="resize:vertical;font-family:inherit"
|
||||
placeholder="Interne Notiz / Zahlungshinweis"></textarea>
|
||||
placeholder="Interne Notiz / Zahlungshinweis">${_esc(p.notes || '')}</textarea>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue