Fix contract adjust storing term end shifted by the server timezone

The date from the edit form was parsed as local midnight, so e.g.
2027-07-04 was stored as 2027-07-03 22:00 UTC, while all other contract
dates use UTC midnight. The audit entry also serialized the previous
term end as {}; both dates are now logged as ISO strings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kundencenter 2026-10-01 23:29:21 +02:00
parent c276eb8ceb
commit 711b99bb34

View file

@ -243,15 +243,15 @@ export const ordersModule: KcModule = {
} catch (e) { if (e instanceof RangeError) throw badRequest(e.message); throw e; }
const renewal = b.renewal ?? k.renewal; const renewalTermMonths = b.renewalTermMonths ?? Number(k.renewal_term_months); const noticeDays = b.noticeDays ?? Number(k.notice_days);
if (renewal === 'auto' && renewalTermMonths < 1) throw badRequest('Bei automatischer Verlängerung muss die Verlängerungsdauer mindestens 1 Monat sein');
const termEnd = b.termEnd === undefined ? k.term_end : b.termEnd === null ? null : new Date(`${b.termEnd}T00:00:00`);
const termEnd = b.termEnd === undefined ? k.term_end : b.termEnd === null ? null : new Date(`${b.termEnd}T00:00:00Z`);
const next = { ...snap, ...price, terms: { ...(snap.terms ?? {}), renewal, renewalTermMonths, noticeDays }, adjusted: { at: new Date().toISOString(), by: a.user.id, reason: b.reason } };
// Neues Laufzeitende = neue Erinnerung fällig
const termChanged = b.termEnd !== undefined && String(termEnd ?? '') !== String(k.term_end ?? '');
await run(`UPDATE contracts SET price_snapshot_json = ?, term_end = ?, renewal = ?, renewal_term_months = ?, notice_days = ?${termChanged ? ', renewal_reminder_sent_at = NULL' : ''} WHERE id = ?`,
[JSON.stringify(next), termEnd, renewal, renewalTermMonths, noticeDays, id], c);
await audit({ actorType: 'user', actorId: a.user.id, orgId: k.org_id, action: 'contract.adjust', resourceType: 'contract', resourceId: id, correlationId: req.correlationId, ip: clientIp(req),
before: { recurring: snap.recurring, setup: snap.setup, basis: snap.basis, interval: snap.interval, discountBp: snap.discountBp, termEnd: k.term_end, renewal: k.renewal, renewalTermMonths: k.renewal_term_months, noticeDays: k.notice_days },
after: { recurring: price.recurring, setup: price.setup, basis: price.basis, interval: price.interval, discountBp: price.discountBp, termEnd, renewal, renewalTermMonths, noticeDays, reason: b.reason } }, c);
before: { recurring: snap.recurring, setup: snap.setup, basis: snap.basis, interval: snap.interval, discountBp: snap.discountBp, termEnd: k.term_end ? new Date(k.term_end).toISOString() : null, renewal: k.renewal, renewalTermMonths: k.renewal_term_months, noticeDays: k.notice_days },
after: { recurring: price.recurring, setup: price.setup, basis: price.basis, interval: price.interval, discountBp: price.discountBp, termEnd: termEnd ? new Date(termEnd).toISOString() : null, renewal, renewalTermMonths, noticeDays, reason: b.reason } }, c);
return { ok: true };
});
});