This commit is contained in:
cay 2026-03-27 14:44:03 +00:00
parent 0edd84923c
commit a6151c6da8
5 changed files with 38 additions and 8 deletions

View File

@ -204,7 +204,8 @@ router.post('/members/:id/update', requireAdmin, async (req, res) => {
const {
salutation, title, first_name, last_name, birth_date,
email, phone, street, address_addition, zip, city,
bank_name, account_holder, iban, tariff_id, status
bank_name, account_holder, iban, tariff_id, status,
agreed_price, agreed_duration
} = req.body;
try {
await db.query(`

View File

@ -127,15 +127,17 @@ router.post('/submit-membership', async (req, res) => {
INSERT INTO memberships
(tariff_id, salutation, title, first_name, last_name, birth_date, email, phone,
street, address_addition, zip, city, bank_name, account_holder, iban,
sepa_accepted, agb_accepted, datenschutz_accepted, data_correct, guardian_consent, is_minor, access_token)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
sepa_accepted, agb_accepted, datenschutz_accepted, data_correct, guardian_consent, is_minor, access_token,
agreed_price, agreed_duration)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, [
tariff_id, salutation, title || '', first_name, last_name, birth_date,
email, phone || '', street, address_addition || '', zip, city,
bank_name || '', account_holder || '', iban || '',
sepa_accepted ? 1 : 0, agb_accepted ? 1 : 0,
datenschutz_accepted ? 1 : 0, data_correct ? 1 : 0,
guardian_consent ? 1 : 0, is_minor, access_token
guardian_consent ? 1 : 0, is_minor, access_token,
tariffs[0].price_monthly, tariffs[0].duration_months
]);
res.json({ success: true });

View File

@ -13,6 +13,9 @@ function calcInvoiceAmount(member, period) {
// Pausiert → 0€
if (member.status === 'paused') return 0;
// Vereinbarten Preis verwenden (nicht den aktuellen Tarif-Preis!)
const price = parseFloat(member.agreed_price || member.price_monthly);
// Erster Monat (anteilig)?
const firstPeriod = member.first_payment_date
? member.first_payment_date.toISOString().substring(0, 7)
@ -22,7 +25,7 @@ function calcInvoiceAmount(member, period) {
return parseFloat(member.first_payment_amt);
}
return parseFloat(member.price_monthly);
return price;
}
// Periode als lesbarer Text: "2026-04" → "April 2026"
@ -74,7 +77,8 @@ router.get('/', requireAdmin, async (req, res) => {
// Vorschau: Mitglieder die noch keine Rechnung für diese Periode haben
const [eligible] = await db.query(`
SELECT m.*, t.price_monthly, t.name as tariff_name
SELECT m.*, t.price_monthly, t.name as tariff_name,
COALESCE(m.agreed_price, t.price_monthly) as agreed_price
FROM memberships m
JOIN tariffs t ON m.tariff_id = t.id
WHERE m.status IN ('active','paused')
@ -119,7 +123,8 @@ router.post('/run', requireAdmin, async (req, res) => {
// Alle aktiven/pausierten Mitglieder im Vertragszeitraum
const [members] = await db.query(`
SELECT m.*, t.price_monthly, t.name as tariff_name
SELECT m.*, t.price_monthly, t.name as tariff_name,
COALESCE(m.agreed_price, t.price_monthly) as agreed_price
FROM memberships m
JOIN tariffs t ON m.tariff_id = t.id
WHERE m.status IN ('active','paused')

View File

@ -200,7 +200,12 @@
<tr>
<td><%= m.last_name %>, <%= m.first_name %></td>
<td><%= m.tariff_name %></td>
<td><strong><%= Number(m.price_monthly).toFixed(2).replace('.', ',') %> €</strong></td>
<td>
<strong><%= Number(m.agreed_price || m.price_monthly).toFixed(2).replace('.', ',') %> €</strong>
<% if (m.agreed_price && Number(m.agreed_price) !== Number(m.price_monthly)) { %>
<br><small class="text-muted" title="Aktueller Tarif-Preis">Tarif: <%= Number(m.price_monthly).toFixed(2).replace('.', ',') %> €</small>
<% } %>
</td>
</tr>
<% }) %>
</tbody>

View File

@ -146,6 +146,23 @@
</select>
</div>
</div>
<div class="karte-row">
<div class="karte-field">
<label>Vereinbarter Preis/Monat <small>(vertraglich)</small></label>
<div class="input-wrap" style="border-radius:6px">
<input type="number" name="agreed_price" step="0.01" min="0"
value="<%= member.agreed_price ? Number(member.agreed_price).toFixed(2) : '' %>"
disabled class="karte-input" style="border:none;padding:5px 0">
<span style="font-size:0.8rem;color:var(--text-muted);padding-right:8px">€</span>
</div>
</div>
<div class="karte-field">
<label>Vereinbarte Laufzeit <small>(Monate)</small></label>
<input type="number" name="agreed_duration" min="1"
value="<%= member.agreed_duration || '' %>"
disabled class="karte-input">
</div>
</div>
<div class="karte-row">
<div class="karte-field">