Tauschanfragen gegen offene Plätze erlauben und Lücken nachbesetzen

Ein Wunschtag ohne Tauschpartner bedeutet jetzt: die Person übernimmt
dort einen offenen Platz; ihr alter Platz wird beim Ausführen sofort
fair nachbesetzt, ohne sie selbst. Rollen werden bei allen Tauschformen
geprüft. Bearbeiter können Person und Wunschtag einer Anfrage nachtragen,
etwa aus der Notiz. Der Anfragedialog zeigt, was am Wunschtag los ist.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
ferdi2goandClaude Fable 5.1 committed 2026-10-15 11:55:53 +00:00
1 parent e8ab95159e
commit 42d495c54e
7 files changed
+196 -30

No files matched your search

+38 -6
View File
@@ -520,10 +520,27 @@ app.put('/api/requests/:id', (req, res) => {
const st = S();
const r = st.requests.find((x) => x.id === req.params.id);
if (!r) return res.status(404).json({ error: 'Anfrage nicht gefunden.' });
const status = String(req.body?.status || '');
if (!REQUEST_STATUS.includes(status)) return bad(res, 'Unbekannter Status.');
r.status = status;
r.resolvedAt = status === 'offen' ? null : new Date().toISOString();
const b = req.body || {};
if (b.status !== undefined) {
const status = String(b.status || '');
if (!REQUEST_STATUS.includes(status)) return bad(res, 'Unbekannter Status.');
r.status = status;
r.resolvedAt = status === 'offen' ? null : new Date().toISOString();
}
// Bearbeiter dürfen den Wunsch nachtragen, z. B. aus dem Notizfeld
if (b.partnerId !== undefined || b.partnerDate !== undefined) {
const v = validateRequest({
personId: r.personId,
date: r.date,
partnerId: b.partnerId !== undefined ? b.partnerId : r.partnerId,
partnerDate: b.partnerDate !== undefined ? b.partnerDate : r.partnerDate,
note: r.note,
}, st.persons, st.assignments);
if (v.error) return bad(res, v.error);
r.partnerId = v.request.partnerId;
r.partnerDate = v.request.partnerDate;
r.role = v.request.role;
}
store.save();
res.json(r);
});
@@ -534,11 +551,26 @@ app.post('/api/requests/:id/execute', (req, res) => {
if (!r) return res.status(404).json({ error: 'Anfrage nicht gefunden.' });
if (r.status !== 'offen') return bad(res, 'Diese Anfrage ist schon abgeschlossen.');
try {
const { changes } = applySwap(st.assignments, r);
const { changes, vacated } = applySwap(st.assignments, r, personsForScheduling());
let filled = null;
if (vacated) {
// Die frei gewordene Stelle gleich nachbesetzen – fair, ohne die anfragende Person
const existing = { [vacated.date]: { ...st.assignments[vacated.date], fill: true } };
const absences = [...st.absences, { personId: r.personId, from: vacated.date, to: vacated.date }];
const history = { ...st.assignments };
delete history[vacated.date];
const generated = generateSchedule({ dates: [vacated.date], persons: personsForScheduling(), absences, existing, history, blocked: blockedDays(), historyWindowDays: st.settings.historyWindowDays });
const picked = generated[vacated.date]?.[vacated.role] || null;
if (picked) {
st.assignments[vacated.date][vacated.role] = picked;
changes.push({ date: vacated.date, role: vacated.role, from: null, to: picked });
}
filled = { date: vacated.date, role: vacated.role, personId: picked };
}
r.status = 'erledigt';
r.resolvedAt = new Date().toISOString();
store.save();
res.json({ request: r, changes, assignments: st.assignments });
res.json({ request: r, changes, filled, assignments: st.assignments });
} catch (err) {
bad(res, err.message);
}