feat(kontingent): Credits-Stellwerte je Lizenz + Lizenz beim Org-Anlegen
Bedienseite für das Credits-Monatskontingent des Monitors. Das Portal setzt jetzt alle Stellwerte, der Monitor bleibt Herr der Laufzeitfelder. - Lizenz-Modal und Budget-Formular um Abrechnungszeitraum (monthly/total), Übertrag-Checkbox und Warnschwelle erweitert, Vorbelegungen auf das beschlossene Modell (10.000 Credits, monatlich, Übertrag an, 0,20 USD). - Neue Organisation kann direkt eine Lizenz mit Kontingent anlegen (OrgCreate.license, verschachtelt). Org- und Lizenz-Insert laufen in EINER Transaktion, schlägt der Lizenz-Teil fehl, entsteht keine halbe Organisation. Gemeinsamer Helper insert_license_row statt Duplikation der Widerruf- und Audit-Logik. - PUT /api/token-usage/budget validiert credits_period (monthly|total) und budget_warning_percent (1 bis 100), credits_rollover wird wie unlimited_budget normalisiert. Laufzeitfelder bleiben bewusst außerhalb der Whitelist. - Overview und Org-Detail rechnen den Rest jetzt gegen verfügbar = Kontingent + Übertrag, dieselbe Bezugsgröße wie der Hard-Stop im Monitor. Neuer read-only Block Laufzeitwerte im Token-Subtab. - Startup-Check loggt einen deutlichen Fehler, wenn die licenses-Tabelle die Credits-Spalten noch nicht hat (falsche Deploy-Reihenfolge). - Migration migrations/2026-07-25_credits_period.py (idempotent, wortgleich mit der Monitor-Migration) für die Portal-Staging-DB, auf Live gilt: Monitor VOR Portal promoten. - 13 neue Modell-Tests, pytest grün (die zwei bekannten shared-Import-Fehlschläge bestehen unverändert seit vorher). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -267,18 +267,35 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="editCreditsTotal">Credits-Kontingent</label>
|
||||
<input type="number" id="editCreditsTotal" placeholder="z.B. 600000">
|
||||
<input type="number" id="editCreditsTotal" placeholder="z.B. 10000">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="editCreditsPeriod">Abrechnungszeitraum</label>
|
||||
<select id="editCreditsPeriod" onchange="onEditPeriodChange()">
|
||||
<option value="monthly">Monatlich (füllt sich zum Monatswechsel neu)</option>
|
||||
<option value="total">Gesamtlaufzeit</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" style="display:flex; align-items:flex-end;">
|
||||
<label style="display:flex; align-items:center; gap:8px; cursor:pointer; margin:0;">
|
||||
<input type="checkbox" id="editCreditsRollover">
|
||||
<span>Übertrag ungenutzter Credits in den Folgemonat</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="editWarnPercent">Warnschwelle (%)</label>
|
||||
<input type="number" id="editWarnPercent" min="1" max="100" placeholder="80">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="editCostPerCredit">Kosten pro Credit (USD)</label>
|
||||
<input type="number" id="editCostPerCredit" step="0.0001" placeholder="z.B. 0.0033">
|
||||
<input type="number" id="editCostPerCredit" step="0.01" placeholder="0.20">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="editBudgetUsd">Budget-Limit (USD)</label>
|
||||
<input type="number" id="editBudgetUsd" step="0.01" placeholder="z.B. 2000">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="editCreditsUsed">Verbrauchte Credits</label>
|
||||
<label for="editCreditsUsed">Verbrauchte Credits (laufende Periode)</label>
|
||||
<input type="number" id="editCreditsUsed" step="0.01" placeholder="Aktueller Verbrauch">
|
||||
</div>
|
||||
<div style="grid-column:1/-1; display:flex; gap:8px; align-items:center;">
|
||||
@@ -288,6 +305,31 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 style="margin-top:24px;">Laufzeitwerte</h3>
|
||||
<div class="card" style="margin-top:12px;">
|
||||
<div class="card-body">
|
||||
<p class="text-secondary" style="margin:0 0 12px; font-size:13px;">
|
||||
Diese Werte verwaltet der Monitor selbst, sie sind hier nicht änderbar.
|
||||
Stand ist die letzte Lizenzprüfung des Monitors, nach einem Monatswechsel
|
||||
können sie kurzzeitig noch die Vorperiode zeigen.
|
||||
</p>
|
||||
<div style="display:grid; grid-template-columns:1fr 1fr 1fr; gap:12px;">
|
||||
<div class="form-group">
|
||||
<label>Übertrag aus dem Vormonat</label>
|
||||
<div id="viewCreditsCarried" style="font-size:16px; font-weight:600;">-</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Laufende Periode</label>
|
||||
<div id="viewCreditsPeriodStart" style="font-size:16px; font-weight:600;">-</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Verfügbar gesamt (Kontingent + Übertrag)</label>
|
||||
<div id="viewCreditsAvailable" style="font-size:16px; font-weight:600;">-</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -574,6 +616,63 @@
|
||||
</select>
|
||||
<small class="text-secondary">Steuert die Ausgabesprache der KI-Pipeline (Lagebild, Faktencheck, Recherche) und die sichtbarsten UI-Strings im Monitor.</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="background:var(--bg-tertiary,#0f172a); padding:8px 12px; border-radius:6px; border:1px solid var(--border);">
|
||||
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;margin:0;">
|
||||
<input type="checkbox" id="newOrgWithLicense" checked onchange="onNewOrgLicenseToggle()">
|
||||
<span><strong>Direkt eine Lizenz mit Credits-Kontingent anlegen</strong></span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="newOrgLicenseFields">
|
||||
<div class="form-group">
|
||||
<label for="newOrgLicType">Lizenztyp</label>
|
||||
<select id="newOrgLicType" onchange="onNewOrgLicTypeChange()">
|
||||
<option value="trial" selected>Trial</option>
|
||||
<option value="annual">Jahreslizenz</option>
|
||||
<option value="permanent">Permanent</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newOrgLicMaxUsers">Maximale Nutzer</label>
|
||||
<input type="number" id="newOrgLicMaxUsers" value="5" min="1" max="1000">
|
||||
</div>
|
||||
<div class="form-group" id="newOrgLicDurationGroup">
|
||||
<label for="newOrgLicDuration">Laufzeit (Tage)</label>
|
||||
<input type="number" id="newOrgLicDuration" value="14" min="1" max="3650">
|
||||
<div class="text-muted mt-8" style="font-size: 12px;">Trial: Standard 14 Tage, Jahreslizenz: Standard 365 Tage</div>
|
||||
</div>
|
||||
<div class="form-group" style="background:var(--bg-tertiary,#0f172a); padding:8px 12px; border-radius:6px; border:1px solid var(--border);">
|
||||
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;margin:0;">
|
||||
<input type="checkbox" id="newOrgLicUnlimited" onchange="onNewOrgLicUnlimitedToggle()">
|
||||
<span><strong>Unbegrenztes Budget</strong> — getrackt, aber kein Hard-Stop</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newOrgLicCreditsTotal">Credits-Kontingent</label>
|
||||
<input type="number" id="newOrgLicCreditsTotal" value="10000" placeholder="z.B. 10000">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newOrgLicCreditsPeriod">Abrechnungszeitraum</label>
|
||||
<select id="newOrgLicCreditsPeriod" onchange="onNewOrgLicPeriodChange()">
|
||||
<option value="monthly" selected>Monatlich (Kontingent füllt sich zum Monatswechsel neu)</option>
|
||||
<option value="total">Gesamtlaufzeit</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;margin:0;">
|
||||
<input type="checkbox" id="newOrgLicRollover" checked>
|
||||
<span>Übertrag ungenutzter Credits in den Folgemonat (gedeckelt auf ein Monatskontingent)</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newOrgLicWarnPercent">Warnschwelle (%)</label>
|
||||
<input type="number" id="newOrgLicWarnPercent" value="80" min="1" max="100">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newOrgLicCostPerCredit">Kosten pro Credit (USD)</label>
|
||||
<input type="number" id="newOrgLicCostPerCredit" step="0.01" value="0.20" placeholder="0.20">
|
||||
</div>
|
||||
</div>
|
||||
<div id="newOrgError" class="error-msg" style="display:none"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@@ -649,11 +748,28 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newLicCreditsTotal">Credits-Kontingent</label>
|
||||
<input type="number" id="newLicCreditsTotal" placeholder="z.B. 600000">
|
||||
<input type="number" id="newLicCreditsTotal" value="10000" placeholder="z.B. 10000">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newLicCreditsPeriod">Abrechnungszeitraum</label>
|
||||
<select id="newLicCreditsPeriod" onchange="onNewLicPeriodChange()">
|
||||
<option value="monthly" selected>Monatlich (Kontingent füllt sich zum Monatswechsel neu)</option>
|
||||
<option value="total">Gesamtlaufzeit</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;margin:0;">
|
||||
<input type="checkbox" id="newLicRollover" checked>
|
||||
<span>Übertrag ungenutzter Credits in den Folgemonat (gedeckelt auf ein Monatskontingent)</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newLicWarnPercent">Warnschwelle (%)</label>
|
||||
<input type="number" id="newLicWarnPercent" value="80" min="1" max="100">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newLicCostPerCredit">Kosten pro Credit (USD)</label>
|
||||
<input type="number" id="newLicCostPerCredit" step="0.0001" placeholder="z.B. 0.0033">
|
||||
<input type="number" id="newLicCostPerCredit" step="0.01" placeholder="0.20">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newLicBudgetUsd">Budget-Limit (USD)</label>
|
||||
@@ -1038,7 +1154,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/js/app.js?v=20260522a"></script>
|
||||
<script src="/static/js/app.js?v=20260725a"></script>
|
||||
<script src="/static/js/sources.js?v=20260522x2"></script>
|
||||
<script src="/static/js/x-scraper.js?v=20260522a"></script>
|
||||
<script src="/static/js/source-health.js?v=20260509l"></script>
|
||||
|
||||
@@ -442,14 +442,42 @@ function setupForms() {
|
||||
e.preventDefault();
|
||||
const errEl = document.getElementById("newOrgError");
|
||||
errEl.style.display = "none";
|
||||
const body = {
|
||||
name: document.getElementById("newOrgName").value,
|
||||
slug: document.getElementById("newOrgSlug").value,
|
||||
output_language: document.getElementById("newOrgLanguage").value || "de",
|
||||
};
|
||||
// Optional direkt eine Lizenz mit Credits-Kontingent mitgeben
|
||||
const withLic = document.getElementById("newOrgWithLicense");
|
||||
if (withLic && withLic.checked) {
|
||||
const licType = document.getElementById("newOrgLicType").value;
|
||||
const lic = {
|
||||
license_type: licType,
|
||||
max_users: parseInt(document.getElementById("newOrgLicMaxUsers").value) || 5,
|
||||
unlimited_budget: !!document.getElementById("newOrgLicUnlimited").checked,
|
||||
// Periode und Uebertrag IMMER senden, sonst laesst sich der
|
||||
// Uebertrag ueber das Formular nie abschalten
|
||||
credits_period: document.getElementById("newOrgLicCreditsPeriod").value,
|
||||
credits_rollover: !!document.getElementById("newOrgLicRollover").checked,
|
||||
};
|
||||
if (licType !== "permanent") {
|
||||
lic.duration_days = parseInt(document.getElementById("newOrgLicDuration").value) || undefined;
|
||||
}
|
||||
if (!lic.unlimited_budget) {
|
||||
const ct = document.getElementById("newOrgLicCreditsTotal");
|
||||
const cpc = document.getElementById("newOrgLicCostPerCredit");
|
||||
if (ct && ct.value) lic.credits_total = parseInt(ct.value);
|
||||
if (cpc && cpc.value) lic.cost_per_credit = parseFloat(cpc.value);
|
||||
}
|
||||
const warn = document.getElementById("newOrgLicWarnPercent");
|
||||
if (warn && warn.value) lic.budget_warning_percent = parseInt(warn.value);
|
||||
body.license = lic;
|
||||
}
|
||||
try {
|
||||
await API.post("/api/orgs", {
|
||||
name: document.getElementById("newOrgName").value,
|
||||
slug: document.getElementById("newOrgSlug").value,
|
||||
output_language: document.getElementById("newOrgLanguage").value || "de",
|
||||
});
|
||||
await API.post("/api/orgs", body);
|
||||
closeModal("modalNewOrg");
|
||||
document.getElementById("newOrgForm").reset();
|
||||
onNewOrgLicenseToggle();
|
||||
loadOrgs();
|
||||
loadDashboard();
|
||||
} catch (err) {
|
||||
@@ -515,6 +543,10 @@ function setupForms() {
|
||||
}
|
||||
const unlimitedEl = document.getElementById('newLicUnlimited');
|
||||
body.unlimited_budget = !!(unlimitedEl && unlimitedEl.checked);
|
||||
// Periode und Uebertrag IMMER senden (nicht nur bei gesetztem Wert),
|
||||
// sonst laesst sich der Uebertrag nie abschalten
|
||||
body.credits_period = document.getElementById('newLicCreditsPeriod').value;
|
||||
body.credits_rollover = !!document.getElementById('newLicRollover').checked;
|
||||
if (!body.unlimited_budget) {
|
||||
const creditsTotal = document.getElementById('newLicCreditsTotal');
|
||||
const costPerCredit = document.getElementById('newLicCostPerCredit');
|
||||
@@ -523,6 +555,8 @@ function setupForms() {
|
||||
if (costPerCredit && costPerCredit.value) body.cost_per_credit = parseFloat(costPerCredit.value);
|
||||
if (budgetUsd && budgetUsd.value) body.token_budget_usd = parseFloat(budgetUsd.value);
|
||||
}
|
||||
const warnEl = document.getElementById('newLicWarnPercent');
|
||||
if (warnEl && warnEl.value) body.budget_warning_percent = parseInt(warnEl.value);
|
||||
try {
|
||||
await API.post("/api/licenses", body);
|
||||
closeModal("modalNewLicense");
|
||||
@@ -818,7 +852,11 @@ async function loadOrgTokenUsage(orgId) {
|
||||
el('tokenCreditsRemaining', '∞ Unbegrenzt');
|
||||
el('tokenBudgetUsd', '—');
|
||||
} else {
|
||||
el('tokenCreditsRemaining', budget.credits_remaining != null ? budget.credits_remaining.toLocaleString('de-DE') : '-');
|
||||
var remainingTxt = budget.credits_remaining != null ? budget.credits_remaining.toLocaleString('de-DE') : '-';
|
||||
if (budget.credits_carried > 0 && budget.credits_remaining != null) {
|
||||
remainingTxt += ' (inkl. ' + Math.round(budget.credits_carried).toLocaleString('de-DE') + ' Übertrag)';
|
||||
}
|
||||
el('tokenCreditsRemaining', remainingTxt);
|
||||
el('tokenBudgetUsd', budget.token_budget_usd != null ? '$' + Number(budget.token_budget_usd).toFixed(2) : '-');
|
||||
}
|
||||
el('tokenCostUsd', usage.total_cost_usd != null ? '$' + Number(usage.total_cost_usd).toFixed(2) : '-');
|
||||
@@ -887,37 +925,109 @@ function fillBudgetForm(budget) {
|
||||
el('editCostPerCredit', budget.cost_per_credit);
|
||||
el('editBudgetUsd', budget.token_budget_usd);
|
||||
el('editCreditsUsed', budget.credits_used ? Math.round(budget.credits_used) : 0);
|
||||
el('editWarnPercent', budget.budget_warning_percent);
|
||||
const periodSel = document.getElementById('editCreditsPeriod');
|
||||
if (periodSel) periodSel.value = budget.credits_period || 'monthly';
|
||||
const rolloverCb = document.getElementById('editCreditsRollover');
|
||||
if (rolloverCb) rolloverCb.checked = !!budget.credits_rollover;
|
||||
onEditPeriodChange();
|
||||
const cb = document.getElementById('editUnlimitedBudget');
|
||||
if (cb) {
|
||||
cb.checked = !!budget.unlimited_budget;
|
||||
onUnlimitedToggle();
|
||||
}
|
||||
|
||||
// Laufzeitwerte (verwaltet der Monitor, hier nur Anzeige)
|
||||
const txt = (id, val) => { const e = document.getElementById(id); if (e) e.textContent = val; };
|
||||
const carried = budget.credits_carried || 0;
|
||||
txt('viewCreditsCarried', carried > 0 ? Math.round(carried).toLocaleString('de-DE') + ' Credits' : 'Kein Übertrag');
|
||||
txt('viewCreditsPeriodStart', budget.credits_period_start || 'Noch nicht initialisiert');
|
||||
txt('viewCreditsAvailable', budget.unlimited_budget
|
||||
? '∞ Unbegrenzt'
|
||||
: (budget.credits_available != null ? Math.round(budget.credits_available).toLocaleString('de-DE') + ' Credits' : '-'));
|
||||
}
|
||||
|
||||
// Unlimited-Toggle: Felder ausgrauen wenn aktiv
|
||||
function onUnlimitedToggle() {
|
||||
const cb = document.getElementById('editUnlimitedBudget');
|
||||
const isUnlimited = cb && cb.checked;
|
||||
['editCreditsTotal','editCostPerCredit','editBudgetUsd'].forEach(function(id) {
|
||||
['editCreditsTotal','editCostPerCredit','editBudgetUsd','editCreditsPeriod','editCreditsRollover','editWarnPercent'].forEach(function(id) {
|
||||
const e = document.getElementById(id);
|
||||
if (e) {
|
||||
e.disabled = isUnlimited;
|
||||
e.style.opacity = isUnlimited ? '0.4' : '1';
|
||||
}
|
||||
});
|
||||
if (!isUnlimited) onEditPeriodChange();
|
||||
}
|
||||
|
||||
// Bei Periode "Gesamtlaufzeit" ist der Monats-Uebertrag bedeutungslos
|
||||
function onEditPeriodChange() {
|
||||
const sel = document.getElementById('editCreditsPeriod');
|
||||
const cb = document.getElementById('editCreditsRollover');
|
||||
if (!sel || !cb) return;
|
||||
const unlimitedCb = document.getElementById('editUnlimitedBudget');
|
||||
cb.disabled = sel.value === 'total' || !!(unlimitedCb && unlimitedCb.checked);
|
||||
cb.parentElement.style.opacity = cb.disabled ? '0.4' : '1';
|
||||
}
|
||||
|
||||
// Unlimited-Toggle im Lizenz-Modal
|
||||
function onNewLicUnlimitedToggle() {
|
||||
const cb = document.getElementById('newLicUnlimited');
|
||||
const isUnlimited = cb && cb.checked;
|
||||
['newLicCreditsTotal','newLicCostPerCredit','newLicBudgetUsd'].forEach(function(id) {
|
||||
['newLicCreditsTotal','newLicCostPerCredit','newLicBudgetUsd','newLicCreditsPeriod','newLicRollover','newLicWarnPercent'].forEach(function(id) {
|
||||
const e = document.getElementById(id);
|
||||
if (e) {
|
||||
e.disabled = isUnlimited;
|
||||
e.style.opacity = isUnlimited ? '0.4' : '1';
|
||||
}
|
||||
});
|
||||
if (!isUnlimited) onNewLicPeriodChange();
|
||||
}
|
||||
|
||||
function onNewLicPeriodChange() {
|
||||
const sel = document.getElementById('newLicCreditsPeriod');
|
||||
const cb = document.getElementById('newLicRollover');
|
||||
if (!sel || !cb) return;
|
||||
cb.disabled = sel.value === 'total';
|
||||
cb.parentElement.style.opacity = cb.disabled ? '0.4' : '1';
|
||||
}
|
||||
|
||||
// === Lizenz-Abschnitt im Org-Modal ===
|
||||
function onNewOrgLicenseToggle() {
|
||||
const cb = document.getElementById('newOrgWithLicense');
|
||||
const fields = document.getElementById('newOrgLicenseFields');
|
||||
if (fields) fields.style.display = (cb && cb.checked) ? '' : 'none';
|
||||
}
|
||||
|
||||
function onNewOrgLicTypeChange() {
|
||||
const type = document.getElementById('newOrgLicType').value;
|
||||
const group = document.getElementById('newOrgLicDurationGroup');
|
||||
if (group) group.style.display = type === 'permanent' ? 'none' : '';
|
||||
if (type !== 'permanent') {
|
||||
document.getElementById('newOrgLicDuration').value = type === 'trial' ? '14' : '365';
|
||||
}
|
||||
}
|
||||
|
||||
function onNewOrgLicUnlimitedToggle() {
|
||||
const cb = document.getElementById('newOrgLicUnlimited');
|
||||
const isUnlimited = cb && cb.checked;
|
||||
['newOrgLicCreditsTotal','newOrgLicCostPerCredit','newOrgLicCreditsPeriod','newOrgLicRollover','newOrgLicWarnPercent'].forEach(function(id) {
|
||||
const e = document.getElementById(id);
|
||||
if (e) {
|
||||
e.disabled = isUnlimited;
|
||||
e.style.opacity = isUnlimited ? '0.4' : '1';
|
||||
}
|
||||
});
|
||||
if (!isUnlimited) onNewOrgLicPeriodChange();
|
||||
}
|
||||
|
||||
function onNewOrgLicPeriodChange() {
|
||||
const sel = document.getElementById('newOrgLicCreditsPeriod');
|
||||
const cb = document.getElementById('newOrgLicRollover');
|
||||
if (!sel || !cb) return;
|
||||
cb.disabled = sel.value === 'total';
|
||||
cb.parentElement.style.opacity = cb.disabled ? '0.4' : '1';
|
||||
}
|
||||
|
||||
async function loadDashboardTokenStats() {
|
||||
@@ -977,6 +1087,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
if (creditsTotal && creditsTotal.value) body.credits_total = parseInt(creditsTotal.value);
|
||||
if (costPerCredit && costPerCredit.value) body.cost_per_credit = parseFloat(costPerCredit.value);
|
||||
if (budgetUsd && budgetUsd.value) body.token_budget_usd = parseFloat(budgetUsd.value);
|
||||
// Periode und Uebertrag IMMER senden (nicht ueber das if-value-Muster),
|
||||
// sonst laesst sich der Uebertrag nie abschalten und die Periode nie
|
||||
// zurueckstellen. Laufzeitfelder (credits_carried, credits_period_start,
|
||||
// budget_warning_sent) sendet das Portal grundsaetzlich NICHT.
|
||||
body.credits_period = document.getElementById('editCreditsPeriod').value;
|
||||
body.credits_rollover = !!document.getElementById('editCreditsRollover').checked;
|
||||
var warnPercent = document.getElementById('editWarnPercent');
|
||||
if (warnPercent && warnPercent.value) body.budget_warning_percent = parseInt(warnPercent.value);
|
||||
}
|
||||
// credits_used immer mitsenden (auch im Unlimited-Modus, fuer Korrekturen)
|
||||
if (creditsUsed && creditsUsed.value !== '') body.credits_used = parseFloat(creditsUsed.value);
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren